دوال الاستدعاء وجحيم الاستدعاءات
افهم البرمجة غير المتزامنة المعتمدة على دوال الاستدعاء، وحدّد مشكلة التداخل المعروفة باسم جحيم الاستدعاءات، وتعرّف إلى سبب تقديم Promises لحلّها.
دوال الاستدعاء وجحيم الاستدعاءات درس مجاني في Frontend Academy على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Frontend Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Frontend Academy 4 دروس في المجموع.
ما هي دوال رد الاتصال؟
دالة رد الاتصال هي دالة تُمرَّر كوسيط إلى دالة أخرى، لكي تُستدعى لاحقًا عند اكتمال عمل ما. دوال رد الاتصال هي النمط الأصلي للبرمجة غير المتزامنة في JavaScript — وقد سبقت Promises.
// Synchronous callback:
[1, 2, 3].forEach(function(n) {
console.log(n);
});
// Async callback:
setTimeout(function() {
console.log('1 second later');
}, 1000);دوال رد الاتصال ذات الخطأ أولًا في Node.js
وحّدت Node.js اصطلاحًا معيّنًا: تتلقى دوال رد الاتصال خطأً كوسيط أول ونتيجةً كوسيط ثانٍ. إذا كانت err تساوي null أو undefined، فقد نجحت العملية.
fs.readFile('data.json', 'utf8', function(err, data) {
if (err) {
console.error('Failed to read file:', err);
return;
}
console.log('File contents:', data);
});الخطوات غير المتزامنة باستخدام دوال رد الاتصال
عندما تعتمد عملية غير متزامنة على نتيجة عملية أخرى، تُضمّنون دوال رد الاتصال داخل بعضها. حتى ثلاث خطوات متسلسلة تبدأ في الظهور بشكل فوضوي.
getUser(userId, function(err, user) {
if (err) return handleError(err);
getOrders(user.id, function(err, orders) {
if (err) return handleError(err);
getInvoice(orders[0].id, function(err, invoice) {
if (err) return handleError(err);
// Finally use invoice
renderInvoice(invoice);
});
});
});جحيم دوال رد الاتصال — هرم الهلاك
يُسمّى التضمين العميق جحيم دوال رد الاتصال أو هرم الهلاك. وتشمل مشكلاته: انزياح الشفرة نحو اليمين مع كل مستوى، وتكرار معالجة الأخطاء، وصعوبة تتبّع التدفق، وصعوبة إعادة الهيكلة.
مشكلات جحيم دوال رد الاتصال
1) تتطلب معالجة الأخطاء كتابة if(err) صراحةً في كل مستوى. 2) يصعب تصحيح الأخطاء — إذ لا تعرض تتبعات مكدس الاستدعاءات السلسلة المنطقية. 3) يصعب قراءة الشفرة الموزعة على طبقات أفقية. 4) من السهل تفويت الأخطاء عن غير قصد.
التخفيف من المشكلة: الدوال المسماة
استخرجوا دوال رد الاتصال المتداخلة إلى دوال مسماة على المستوى الأعلى. يؤدي ذلك إلى تسطيح التداخل ويجعل اختبار كل خطوة بمعزل عن غيرها ممكنًا — لكنه لا يحل مشكلة التنسيق الأساسية.
function onInvoice(err, invoice) {
if (err) return handleError(err);
renderInvoice(invoice);
}
function onOrders(err, orders) {
if (err) return handleError(err);
getInvoice(orders[0].id, onInvoice);
}
function onUser(err, user) {
if (err) return handleError(err);
getOrders(user.id, onOrders);
}
getUser(userId, onUser);مكتبات تدفق التحكم (تاريخيًا)
قدّمت مكتبات مثل async.js دوال مساعدة (async.waterfall وasync.parallel) لإدارة التدفقات المعتمدة على دوال رد الاتصال. وكانت هذه المكتبات تمثل أحدث ما توصلت إليه التقنية قبل ظهور Promises في ES2015.
لماذا لن تختفي دوال رد الاتصال
ما زالت دوال رد الاتصال تظهر في كل مكان: إذ تستخدمها مستمعات الأحداث وطرائق المصفوفات وsetTimeout وواجهات تدفق البيانات. الفكرة الأساسية هي أن Promises وasync/await أفضل في تسلسل العمليات غير المتزامنة. أما دوال رد الاتصال للأحداث البسيطة التي تحدث مرة واحدة فتظل خيارًا مناسبًا.
من دالة رد اتصال إلى Promise: Promisify
تغلف util.promisify() في Node.js دوال رد الاتصال ذات الخطأ أولًا، وتحولها إلى إصدارات تعيد Promise. ويتيح ذلك الربط بين واجهات البرمجة القديمة وشفرة async/await الحديثة.
const { promisify } = require('util');
const readFile = promisify(require('fs').readFile);
async function readConfig() {
const data = await readFile('config.json', 'utf8');
return JSON.parse(data);
}متى تظل دوال رد الاتصال الخيار المناسب
تتوقع مستمعات الأحداث (addEventListener) دوال رد اتصال، وهذا مناسب — فهي تُستدعى عدة مرات. كما تتوقع معالجات بيانات التدفق دوال رد اتصال. تمثل Promises نتائج غير متزامنة تحدث مرة واحدة، بينما تمثل دوال رد الاتصال اشتراكات في أحداث متعددة.
الطريق إلى الأمام: Promises وasync/await
حلّت Promises التي ظهرت في ES2015 وasync/await التي ظهرت في ES2017 مشكلة جحيم دوال رد الاتصال. تستخدم شفرة JavaScript الحديثة في الغالب async/await للعمليات غير المتزامنة المتسلسلة. يظل فهم دوال رد الاتصال مهمًا لقراءة الشفرة القديمة وفهم الأجزاء الداخلية للمكتبات.
اختبار سريع
ما المشكلة الأساسية التي يصفها مصطلح جحيم دوال رد الاتصال؟
مراجعة: دوال رد الاتصال
دوال رد الاتصال هي دوال تُمرَّر إلى دوال أخرى لكي تُستدعى لاحقًا. وهي النمط الأصلي للبرمجة غير المتزامنة. دوال رد الاتصال ذات الخطأ أولًا (err، result) هي اصطلاح Node.js. يؤدي التداخل العميق إلى جحيم دوال رد الاتصال. استخرجوا الدوال المسماة لتسطيح التداخل. استخدموا Promises وasync/await للتدفقات غير المتزامنة المتسلسلة. تظل دوال رد الاتصال مناسبة لمستمعات الأحداث وتدفقات البيانات.
الأسئلة الشائعة
هل درس «دوال الاستدعاء وجحيم الاستدعاءات» مجاني؟
نعم — نص درس «دوال الاستدعاء وجحيم الاستدعاءات» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Frontend Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Frontend Academy 4 دروس في المجموع.
ماذا ستتعلم في «دوال الاستدعاء وجحيم الاستدعاءات»؟
افهم البرمجة غير المتزامنة المعتمدة على دوال الاستدعاء، وحدّد مشكلة التداخل المعروفة باسم جحيم الاستدعاءات، وتعرّف إلى سبب تقديم Promises لحلّها. تتمرن على Frontend Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Frontend Academy؟
لا تُشترط خبرة سابقة. Frontend Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.
كم من الوقت يستغرق درس «دوال الاستدعاء وجحيم الاستدعاءات»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Frontend Academy هذا؟
نعم. كل درس في Frontend Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- حلقة الأحداث: مكدس الاستدعاءات وطابور المهام والمهام المصغّرة
- دوال الاستدعاء وجحيم الاستدعاءات
- Promises: then وcatch وfinally وPromise.all
- async/await ومعالجة الأخطاء