مقدمة في البرمجة غير المتزامنة في JavaScript
\n\nJavaScript هي لغة برمجة أحادية الخيط. وهذا يعني أنه في كل لحظة زمنية يتم تنفيذ عملية واحدة فقط. ومع ذلك، إذا تم تنفيذ جميع العمليات بشكل تسلسلي، فسيحدث "تجميد" للمتصفح عند تحميل البيانات من الخادم أو قراءة ملف. وهنا يأتي دور البرمجة غير المتزامنة.
\n\nتسمح البرمجة غير المتزامنة بتنفيذ العمليات الطويلة (طلبات API، قراءة الملفات، المؤقتات) دون حظر سير التنفيذ الرئيسي. بدلاً من انتظار اكتمال المهمة، يستمر الكود في التنفيذ، وتتم معالجة النتيجة لاحقًا. هذا مفهوم أساسي لإنشاء تطبيقات سريعة وسريعة الاستجابة على Node.js.
\n\nفي هذا الدليل، سنشرح ثلاثة مناهج رئيسية للبرمجة غير المتزامنة: دوال الاسترجاع (Callbacks)، والوعود (Promises)، وبناء الجملة async/await. ستتعلم كيفية العمل بشكل صحيح مع الكود غير المتزامن وتجنب الأخطاء الشائعة للمبتدئين.
1. دوال الاسترجاع (Callbacks): أساس عدم التزامن
\n\nدالة الاسترجاع (callback) هي دالة يتم تمريرها كوسيطة لدالة أخرى ويتم استدعاؤها بعد اكتمال العملية غير المتزامنة. هذه هي أقدم وأبسط طريقة للعمل مع عدم التزامن في JavaScript.
\n\nمثال مع setTimeout
\n\nالدالة المدمجة setTimeout هي مثال كلاسيكي لعملية غير متزامنة. تأخذ دالة استرجاع وتأخيرًا بالمللي ثانية:
console.log('البداية');\n\nsetTimeout(() => {\n console.log('مرت ثانيتان');\n}, 2000);\n\nconsole.log('النهاية');\n\n// الإخراج:\n// البداية\n// النهاية\n// مرت ثانيتان\n\nلاحظ: "النهاية" تُطبع قبل الرسالة من دالة الاسترجاع. هذا هو عدم التزامن - الكود لا ينتظر اكتمال setTimeout.
مشكلة "جحيم دوال الاسترجاع" (Callback Hell)
\n\nعندما يكون لديك العديد من العمليات غير المتزامنة المتداخلة، يصبح الكود صعب القراءة. تُسمى هذه الظاهرة "جحيم دوال الاسترجاع" (Callback Hell):
\n\nfs.readFile('file1.txt', (err, data1) => {\n if (err) throw err;\n fs.readFile('file2.txt', (err, data2) => {\n if (err) throw err;\n fs.readFile('file3.txt', (err, data3) => {\n if (err) throw err;\n console.log(data1, data2, data3);\n });\n });\n});\n\nمن الصعب صيانة وتصحيح مثل هذا الكود. لحسن الحظ، تقدم JavaScript الحديثة حلولاً أكثر أناقة.
\n\n2. الوعود (Promises): معالجة أنيقة لعدم التزامن
\n\nPromise (الوعد) هو كائن يمثل نتيجة عملية غير متزامنة. يمكن أن يكون الوعد في إحدى ثلاث حالات:
\n\n- \n
- Pending (معلق) - الحالة الأولية، العملية لم تكتمل بعد \n
- Fulfilled (مُنجز) - اكتملت العملية بنجاح \n
- Rejected (مرفوض) - اكتملت العملية بخطأ \n
إنشاء واستخدام Promise
\n\nconst myPromise = new Promise((resolve, reject) => {\n setTimeout(() => {\n const success = true;\n if (success) {\n resolve('تم الحصول على البيانات');\n } else {\n reject('خطأ في الحصول على البيانات');\n }\n }, 2000);\n});\n\nmyPromise\n .then(result => {\n console.log(result); // "تم الحصول على البيانات"\n })\n .catch(error => {\n console.error(error); // "خطأ في الحصول على البيانات"\n })\n .finally(() => {\n console.log('اكتملت العملية');\n });\n\nسلسلة الوعود (Chaining)
\n\nيمكن ربط الوعود في سلاسل، مما يسمح بتجنب "جحيم دوال الاسترجاع":
\n\nfunction readFile(filename) {\n return new Promise((resolve, reject) => {\n fs.readFile(filename, 'utf8', (err, data) => {\n if (err) reject(err);\n else resolve(data);\n });\n });\n}\n\nreadFile('file1.txt')\n .then(data1 => {\