logo

رد الاتصال الجحيم في جافا سكريبت

JavaScript هي لغة برمجة غير متزامنة (غير محظورة) وذات خيط واحد، مما يعني أنه يمكن تشغيل عملية واحدة فقط في كل مرة.

في لغات البرمجة، يشير رد الاتصال الجحيم بشكل عام إلى طريقة غير فعالة لكتابة التعليمات البرمجية باستخدام مكالمات غير متزامنة. ويُعرف أيضًا باسم هرم الموت.

يُشار إلى جحيم رد الاتصال في JavaScript على أنه موقف يتم فيه تنفيذ عدد كبير جدًا من وظائف رد الاتصال المتداخلة. فهو يقلل من إمكانية قراءة التعليمات البرمجية وصيانتها. تحدث حالة جحيم رد الاتصال عادةً عند التعامل مع عمليات الطلب غير المتزامنة، مثل تقديم طلبات متعددة لواجهة برمجة التطبيقات (API) أو التعامل مع الأحداث ذات التبعيات المعقدة.

لفهم جحيم رد الاتصال في JavaScript بشكل أفضل، عليك أولاً فهم عمليات الاسترجاعات وحلقات الأحداث في JavaScript.

عمليات الاسترجاعات في جافا سكريبت

تعتبر جافا سكريبت كل شيء ككائن، مثل السلاسل والمصفوفات والوظائف. وبالتالي، فإن مفهوم رد الاتصال يسمح لنا بتمرير الوظيفة كوسيطة إلى وظيفة أخرى. ستكمل وظيفة رد الاتصال التنفيذ أولاً، وسيتم تنفيذ الوظيفة الأصلية لاحقًا.

يتم تنفيذ وظائف رد الاتصال بشكل غير متزامن وتسمح للتعليمات البرمجية بمواصلة التشغيل دون انتظار إكمال المهمة غير المتزامنة. عندما يتم دمج عدة مهام غير متزامنة، وتعتمد كل مهمة على مهمتها السابقة، تصبح بنية التعليمات البرمجية معقدة.

دعونا نفهم استخدام وأهمية عمليات الاسترجاعات. لنفترض على سبيل المثال أن لدينا دالة تأخذ ثلاث معلمات، سلسلة واحدة ورقمين. نريد بعض المخرجات بناءً على نص السلسلة بشروط متعددة.

خذ بعين الاعتبار المثال أدناه:

 function expectedResult(action, x, y){ if(action === 'add'){ return x+y }else if(action === 'subtract'){ return x-y } } console.log(expectedResult('add',20,10)) console.log(expectedResult('subtract',30,10)) 

انتاج:

 30 20 

سيعمل الكود أعلاه بشكل جيد، لكننا بحاجة إلى إضافة المزيد من المهام لجعل الكود قابلاً للتطوير. سيستمر أيضًا عدد العبارات الشرطية في التزايد، مما سيؤدي إلى بنية تعليمات برمجية فوضوية تحتاج إلى تحسين وقابلة للقراءة.

لذلك يمكننا إعادة كتابة الكود بطريقة أفضل كالتالي:

 function add(x,y){ return x+y } function subtract(x,y){ return x-y } function expectedResult(callBack, x, y){ return callBack(x,y) } console.log(expectedResult(add, 20, 10)) console.log(expectedResult(subtract, 30, 10)) 

انتاج:

 30 20 

ومع ذلك، فإن الناتج سيكون هو نفسه. لكن في المثال أعلاه، قمنا بتعريف نص وظيفتها المنفصلة وقمنا بتمرير الوظيفة كوظيفة رد اتصال إلى الوظيفة المتوقعة. ومن ثم، إذا أردنا توسيع وظيفة النتائج المتوقعة حتى نتمكن من إنشاء جسم عامل آخر بعملية مختلفة واستخدامه كوظيفة رد اتصال، فسيسهل ذلك فهم وتحسين إمكانية قراءة التعليمات البرمجية.

هناك أمثلة أخرى مختلفة لعمليات الاسترجاعات المتوفرة في ميزات JavaScript المدعومة. بعض الأمثلة الشائعة هي مستمعي الأحداث ووظائف المصفوفة مثل الخريطة والتقليل والتصفية وما إلى ذلك.

لفهم ذلك بشكل أفضل، يجب أن نفهم قيمة التمرير ومرجع التمرير في JavaScript.

تدعم JavaScript نوعين من أنواع البيانات البدائية وغير البدائية. أنواع البيانات الأولية هي غير محددة، وخالية، وسلسلة، ومنطقية، ولا يمكن تغييرها، أو يمكننا القول أنها غير قابلة للتغيير نسبيًا؛ أنواع البيانات غير البدائية هي المصفوفات والوظائف والكائنات التي يمكن تغييرها أو تغييرها.

يمرر التمرير حسب المرجع العنوان المرجعي للكيان، مثل الدالة التي يمكن اعتبارها وسيطة. لذلك، إذا تم تغيير القيمة داخل تلك الدالة، فسوف تتغير القيمة الأصلية المتوفرة خارج الدالة.

وبالمقارنة، فإن مفهوم التمرير بالقيمة لا يغير قيمته الأصلية، المتوفرة خارج جسم الوظيفة. بدلاً من ذلك، سيتم نسخ القيمة إلى موقعين مختلفين باستخدام الذاكرة الخاصة بهم. حددت جافا سكريبت جميع الكائنات حسب مرجعها.

في JavaScript، يستمع addEventListener إلى الأحداث مثل النقر، وتمرير الماوس، وإخراج الماوس، ويأخذ الوسيطة الثانية كدالة سيتم تنفيذها بمجرد تشغيل الحدث. يتم استخدام هذه الوظيفة من خلال مفهوم التمرير المرجعي ويتم تمريرها باستخدام بدون قوسين.

النظر في المثال أدناه؛ في هذا المثال، قمنا بتمرير وظيفة الترحيب كوسيطة إلى addEventListener كوظيفة رد الاتصال. سيتم استدعاؤه عند تشغيل حدث النقر:

اختبار.html:

 Javascript Callback Example <h3>Javascript Callback</h3> Click Here to Console const button = document.getElementById(&apos;btn&apos;); const greet=()=&gt;{ console.log(&apos;Hello, How are you?&apos;) } button.addEventListener(&apos;click&apos;, greet) 

انتاج:

رد الاتصال الجحيم في جافا سكريبت

في المثال أعلاه، قمنا بتمرير وظيفة الترحيب كوسيطة إلى addEventListener كوظيفة رد الاتصال. سيتم استدعاؤه عند تشغيل حدث النقر.

وبالمثل، يعد المرشح أيضًا مثالاً على وظيفة رد الاتصال. إذا استخدمنا مرشحًا لتكرار مصفوفة، فسوف يستغرق الأمر وظيفة رد اتصال أخرى كوسيطة لمعالجة بيانات المصفوفة. النظر في المثال أدناه؛ في هذا المثال، نستخدم الدالة الأكبر لطباعة الرقم الأكبر من 5 في المصفوفة. نحن نستخدم الدالة isGreater كدالة رد اتصال في طريقة التصفية.

 const arr = [3,10,6,7] const isGreater = num =&gt; num &gt; 5 console.log(arr.filter(isGreater)) 

انتاج:

 [ 10, 6, 7 ] 

يوضح المثال أعلاه أن الوظيفة الأكبر تُستخدم كوظيفة رد اتصال في طريقة التصفية.

لفهم عمليات الاسترجاعات وحلقات الأحداث في JavaScript بشكل أفضل، دعنا نناقش JavaScript المتزامن وغير المتزامن:

جافا سكريبت متزامن

دعونا نفهم ما هي ميزات لغة البرمجة المتزامنة. تتميز البرمجة المتزامنة بالميزات التالية:

منع التنفيذ: تدعم لغة البرمجة المتزامنة تقنية التنفيذ المنع مما يعني أنها تمنع تنفيذ العبارات اللاحقة التي سيتم تنفيذ العبارات الموجودة بها. وبالتالي فإنها تحقق التنفيذ الحتمي والمتوقع للبيانات.

التدفق المتسلسل: تدعم البرمجة المتزامنة التدفق المتسلسل للتنفيذ، مما يعني أن كل عبارة يتم تنفيذها بطريقة تسلسلية مثل واحدة تلو الأخرى. ينتظر برنامج اللغة حتى يكتمل البيان قبل الانتقال إلى البيان التالي.

بساطة: في كثير من الأحيان، تعتبر البرمجة المتزامنة سهلة الفهم لأنه يمكننا التنبؤ بترتيب تدفق التنفيذ. بشكل عام، إنه خطي وسهل التنبؤ به. من الجيد تطوير التطبيقات الصغيرة بهذه اللغات لأنها تستطيع التعامل مع الترتيب الحرج للعمليات.

معالجة الأخطاء المباشرة: من السهل جدًا معالجة الأخطاء في لغة البرمجة المتزامنة. إذا حدث خطأ أثناء تنفيذ عبارة ما، فسيؤدي ذلك إلى حدوث خطأ ويمكن للبرنامج اكتشافه.

باختصار، تحتوي البرمجة المتزامنة على ميزتين أساسيتين، أي يتم تنفيذ مهمة واحدة في كل مرة، ولن تتم معالجة المجموعة التالية من المهام التالية إلا بمجرد انتهاء المهمة الحالية. وبالتالي فإنه يتبع تنفيذ التعليمات البرمجية التسلسلية.

هذا السلوك للبرمجة عند تنفيذ عبارة، تخلق اللغة حالة من كود الكتلة حيث يجب على كل مهمة الانتظار حتى تكتمل المهمة السابقة.

ولكن عندما يتحدث الناس عن جافا سكريبت، كانت الإجابة محيرة دائمًا سواء كانت متزامنة أو غير متزامنة.

الأرقام الرومانية 1-100

في الأمثلة التي تمت مناقشتها أعلاه، عندما استخدمنا وظيفة كرد اتصال في وظيفة التصفية، تم تنفيذها بشكل متزامن. ومن ثم يطلق عليه التنفيذ المتزامن. يجب أن تنتظر وظيفة التصفية حتى تنتهي الوظيفة الأكبر من تنفيذها.

ومن ثم، تسمى وظيفة رد الاتصال أيضًا بحظر عمليات الاسترجاعات، لأنها تمنع تنفيذ الوظيفة الأصلية التي تم استدعاءها فيها.

في المقام الأول، تعتبر جافا سكريبت متزامنة ذات ترابط واحد وحظر بطبيعتها. ولكن باستخدام بعض الأساليب، يمكننا أن نجعلها تعمل بشكل غير متزامن بناءً على سيناريوهات مختلفة.

الآن، دعونا نفهم جافا سكريبت غير المتزامن.

جافا سكريبت غير المتزامن

تركز لغة البرمجة غير المتزامنة على تحسين أداء التطبيق. يمكن استخدام عمليات الاسترجاعات في مثل هذه السيناريوهات. يمكننا تحليل السلوك غير المتزامن لجافا سكريبت من خلال المثال التالي:

 function greet(){ console.log(&apos;greet after 1 second&apos;) } setTimeout(greet, 1000) 

من المثال أعلاه، تأخذ الدالة setTimeout رد اتصال ووقتًا بالمللي ثانية كوسيطات. يتم استدعاء رد الاتصال بعد الوقت المذكور (هنا 1 ثانية). باختصار، ستنتظر الوظيفة 1s حتى يتم تنفيذها. الآن، ألق نظرة على الكود أدناه:

 function greet(){ console.log(&apos;greet after 1 second&apos;) } setTimeout(greet, 1000) console.log(&apos;first&apos;) console.log(&apos;Second&apos;) 

انتاج:

 first Second greet after 1 second 

من الكود أعلاه، سيتم تنفيذ رسائل السجل بعد setTimeout أولاً أثناء مرور الموقت. ومن ثم، فإنه ينتج ثانية واحدة ثم رسالة الترحيب بعد فاصل زمني قدره ثانية واحدة.

في جافا سكريبت، setTimeout هي وظيفة غير متزامنة. عندما نستدعي وظيفة setTimeout، فإنها تسجل وظيفة رد الاتصال (الترحيب في هذه الحالة) ليتم تنفيذها بعد التأخير المحدد. ومع ذلك، فإنه لا يمنع تنفيذ التعليمات البرمجية اللاحقة.

في المثال أعلاه، رسائل السجل هي العبارات المتزامنة التي يتم تنفيذها على الفور. فهي لا تعتمد على وظيفة setTimeout. لذلك، يقومون بتنفيذ وتسجيل الرسائل الخاصة بهم إلى وحدة التحكم دون انتظار التأخير المحدد في setTimeout.

وفي الوقت نفسه، تعالج حلقة الأحداث في JavaScript المهام غير المتزامنة. في هذه الحالة، ينتظر مرور التأخير المحدد (ثانية واحدة)، وبعد انقضاء ذلك الوقت، يلتقط وظيفة رد الاتصال (الترحيب) وينفذها.

وبالتالي، تم تنفيذ الكود الآخر بعد وظيفة setTimeout أثناء التشغيل في الخلفية. يسمح هذا السلوك لـ JavaScript بتنفيذ مهام أخرى أثناء انتظار اكتمال العملية غير المتزامنة.

نحن بحاجة إلى فهم مكدس الاستدعاءات وقائمة انتظار رد الاتصال للتعامل مع الأحداث غير المتزامنة في JavaScript.

النظر في الصورة أدناه:

رد الاتصال الجحيم في جافا سكريبت

من الصورة أعلاه، يتكون محرك JavaScript النموذجي من ذاكرة كومة ومكدس استدعاءات. تقوم مكدس الاستدعاءات بتنفيذ كافة التعليمات البرمجية دون الانتظار عند دفعها إلى المكدس.

ذاكرة الكومة مسؤولة عن تخصيص الذاكرة للكائنات والوظائف في وقت التشغيل عند الحاجة إليها.

الآن، تتكون محركات المتصفح لدينا من العديد من واجهات برمجة تطبيقات الويب مثل DOM، وsetTimeout، ووحدة التحكم، والجلب، وما إلى ذلك، ويمكن للمحرك الوصول إلى واجهات برمجة التطبيقات هذه باستخدام كائن النافذة العامة. في الخطوة التالية، تلعب بعض حلقات الأحداث دور حارس البوابة الذي يختار طلبات الوظائف داخل قائمة انتظار رد الاتصال ويدفعها إلى المكدس. تتطلب هذه الوظائف، مثل setTimeout، وقت انتظار معين.

الآن، لنعد إلى مثالنا، الدالة setTimeout؛ عند مواجهة الوظيفة، يتم تسجيل المؤقت في قائمة انتظار رد الاتصال. بعد ذلك، يتم دفع بقية التعليمات البرمجية إلى مكدس الاستدعاءات ويتم تنفيذها بمجرد وصول الوظيفة إلى حد المؤقت الخاص بها، وتنتهي صلاحيتها، وتدفع قائمة انتظار رد الاتصال وظيفة رد الاتصال، التي لها المنطق المحدد والمسجلة في وظيفة المهلة . وبالتالي سيتم تنفيذه بعد الوقت المحدد.

سيناريوهات الجحيم رد الاتصال

لقد ناقشنا الآن موضوعات الاسترجاعات والمتزامنة وغير المتزامنة وغيرها من الموضوعات ذات الصلة بجحيم رد الاتصال. دعونا نفهم ما هو رد الاتصال الجحيم في جافا سكريبت.

يُعرف الموقف الذي يتم فيه تداخل عمليات رد الاتصال المتعددة باسم جحيم رد الاتصال نظرًا لأن شكل الكود الخاص به يشبه الهرم، والذي يُطلق عليه أيضًا 'هرم الهلاك'.

يجعل جحيم رد الاتصال من الصعب فهم الكود والحفاظ عليه. يمكننا أن نرى هذا الموقف في الغالب أثناء العمل في العقدة JS. على سبيل المثال، خذ بعين الاعتبار المثال التالي:

 getArticlesData(20, (articles) =&gt; { console.log(&apos;article lists&apos;, articles); getUserData(article.username, (name) =&gt; { console.log(name); getAddress(name, (item) =&gt; { console.log(item); //This goes on and on... } }) 

في المثال أعلاه، يأخذ getUserData اسم مستخدم يعتمد على قائمة المقالات أو يحتاج إلى استخراج استجابة getArticles الموجودة داخل المقالة. لدى getAddress أيضًا تبعية مماثلة، والتي تعتمد على استجابة getUserData. هذا الوضع يسمى جحيم رد الاتصال.

يمكن فهم العمل الداخلي لجحيم رد الاتصال من خلال المثال التالي:

دعونا نفهم أننا بحاجة إلى تنفيذ المهمة أ. ولتنفيذ مهمة، نحتاج إلى بعض البيانات من المهمة ب. لدينا مهام مختلفة تعتمد على بعضها البعض ويتم تنفيذها بشكل غير متزامن. وبالتالي، فإنه يخلق سلسلة من وظائف رد الاتصال.

دعونا نفهم الوعود في JavaScript وكيف تنشئ عمليات غير متزامنة، مما يسمح لنا بتجنب كتابة عمليات رد اتصال متداخلة.

وعود جافا سكريبت

في JavaScript، تم تقديم الوعود في ES6. إنه كائن ذو طلاء نحوي. نظرًا لسلوكها غير المتزامن، فهي طريقة بديلة لتجنب كتابة عمليات الاسترجاعات للعمليات غير المتزامنة. في الوقت الحاضر، يتم تنفيذ واجهات برمجة تطبيقات الويب مثل fetch() باستخدام الواعدة، والتي توفر طريقة فعالة للوصول إلى البيانات من الخادم. كما أنه أدى إلى تحسين إمكانية قراءة التعليمات البرمجية وهو وسيلة لتجنب كتابة عمليات الاسترجاعات المتداخلة.

تعبر الوعود في الحياة الواقعية عن الثقة بين شخصين أو أكثر والتأكيد بأن شيئًا معينًا سيحدث بالتأكيد. في JavaScript، الوعد هو كائن يضمن إنتاج قيمة واحدة في المستقبل (عند الحاجة). يتم استخدام Promise في JavaScript لإدارة ومعالجة العمليات غير المتزامنة.

يقوم Promise بإرجاع كائن يضمن ويمثل اكتمال أو فشل العمليات غير المتزامنة ومخرجاتها. إنه وكيل لقيمة دون معرفة الإخراج الدقيق. من المفيد للإجراءات غير المتزامنة توفير قيمة نجاح نهائية أو سبب فشل. وبالتالي، فإن الطرق غير المتزامنة ترجع القيم مثل الطريقة المتزامنة.

بشكل عام، الوعود لها الحالات الثلاث التالية:

  • تم الاستيفاء: حالة الاستيفاء هي عندما يتم حل الإجراء المطبق أو إكماله بنجاح.
  • معلق: الحالة المعلقة هي عندما يكون الطلب قيد المعالجة، ولم يتم حل الإجراء المطبق أو رفضه ولا يزال في حالته الأولية.
  • مرفوض: الحالة المرفوضة هي عندما يتم رفض الإجراء المطبق، مما يتسبب في فشل العملية المطلوبة. يمكن أن يكون سبب الرفض أي شيء، بما في ذلك تعطل الخادم.

صيغة الوعود:

 let newPromise = new Promise(function(resolve, reject) { // asynchronous call is made //Resolve or reject the data }); 

فيما يلي مثال لكتابة الوعود:

وهذا مثال على كتابة الوعد.

الحذف من شجرة البحث الثنائية
 function getArticleData(id) { return new Promise((resolve, reject) =&gt; { setTimeout(() =&gt; { console.log(&apos;Fetching data....&apos;); resolve({ id: id, name: &apos;derik&apos; }); }, 5000); }); } getArticleData(&apos;10&apos;).then(res=&gt; console.log(res)) 

في المثال أعلاه، يمكننا أن نرى كيف يمكننا استخدام الوعود بكفاءة لتقديم طلب من الخادم. يمكننا أن نلاحظ أن إمكانية قراءة الكود في الكود أعلاه زادت مقارنة بعمليات الاسترجاعات. توفر الوعود توابع مثل .then() و .catch()، والتي تسمح لنا بالتعامل مع حالة العملية في حالة النجاح أو الفشل. يمكننا تحديد الحالات لمختلف حالات الوعود.

غير متزامن/انتظار في جافا سكريبت

إنها طريقة أخرى لتجنب استخدام عمليات الاسترجاعات المتداخلة. يتيح لنا Async/Await استخدام الوعود بشكل أكثر كفاءة. يمكننا تجنب استخدام تسلسل أسلوب .then() أو .catch(). تعتمد هذه الأساليب أيضًا على وظائف رد الاتصال.

يمكن استخدام Async/Await بدقة مع Promise لتحسين أداء التطبيق. لقد حلت الوعود داخليًا وقدمت النتيجة. وأيضًا، مرة أخرى، فهي أكثر قابلية للقراءة من التابعين () أو Catch().

لا يمكننا استخدام Async/Await مع وظائف رد الاتصال العادية. لاستخدامها، يجب أن نجعل الوظيفة غير متزامنة عن طريق كتابة كلمة أساسية غير متزامنة قبل الكلمة الأساسية للوظيفة. ومع ذلك، داخليًا يستخدم أيضًا التسلسل.

فيما يلي مثال على Async/Await:

 async function displayData() { try { const articleData = await getArticle(10); const placeData = await getPlaces(article.name); const cityData = await getCity(place) console.log(city); } catch (err) { console.log(&apos;Error: &apos;, err.message); } } displayData(); 

لاستخدام Async/Await، يجب تحديد الوظيفة باستخدام الكلمة الأساسية غير المتزامنة، ويجب كتابة الكلمة الأساسية انتظار داخل الوظيفة. سيوقف المزامنة تنفيذه حتى يتم حل الوعد أو رفضه. سيتم استئنافه عندما يتم التعامل مع الوعد. بمجرد حلها، سيتم تخزين قيمة تعبير الانتظار في المتغير الذي يحملها.

ملخص:

باختصار، يمكننا تجنب عمليات الاسترجاعات المتداخلة باستخدام الوعود والمزامنة/الانتظار. وبصرف النظر عن ذلك، يمكننا اتباع أساليب أخرى، مثل كتابة التعليقات، وتقسيم الكود إلى مكونات منفصلة يمكن أن يكون مفيدًا أيضًا. لكن في الوقت الحاضر، يفضل المطورون استخدام المزامنة/الانتظار.

خاتمة:

يُشار إلى جحيم رد الاتصال في JavaScript على أنه موقف يتم فيه تنفيذ عدد كبير جدًا من وظائف رد الاتصال المتداخلة. فهو يقلل من إمكانية قراءة التعليمات البرمجية وصيانتها. تحدث حالة جحيم رد الاتصال عادةً عند التعامل مع عمليات الطلب غير المتزامنة، مثل تقديم طلبات متعددة لواجهة برمجة التطبيقات (API) أو التعامل مع الأحداث ذات التبعيات المعقدة.

لفهم جحيم رد الاتصال بشكل أفضل في JavaScript.

تعتبر جافا سكريبت كل شيء ككائن، مثل السلاسل والمصفوفات والوظائف. وبالتالي، فإن مفهوم رد الاتصال يسمح لنا بتمرير الوظيفة كوسيطة إلى وظيفة أخرى. ستكمل وظيفة رد الاتصال التنفيذ أولاً، وسيتم تنفيذ الوظيفة الأصلية لاحقًا.

يتم تنفيذ وظائف رد الاتصال بشكل غير متزامن وتسمح للتعليمات البرمجية بمواصلة التشغيل دون انتظار إكمال المهمة غير المتزامنة.