البرمجة غير المتزامنة في JavaScript: دليل شامل

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة في البرمجة غير المتزامنة في JavaScript



JavaScript هي لغة ذات خيط واحد مع حلقة أحداث غير محظورة (Event Loop). هذا يعني أنه في أي لحظة زمنية يتم تنفيذ عملية واحدة فقط، ولكن بفضل عدم التزامن يمكننا عدم انتظار اكتمال المهام البطيئة (قراءة ملف، طلب API، مؤقت)، بل مواصلة تنفيذ كود آخر. في هذه المقالة سنشرح جميع المفاهيم الأساسية للبرمجة غير المتزامنة في بيئة Node.js، من دوال الاسترجاع إلى الصياغة الحديثة async/await.



البرمجة غير المتزامنة تتيح إنشاء تطبيقات خادم عالية الأداء تعالج آلاف الطلبات دون حظر الخيط الرئيسي. فهم هذا النموذج هو مهارة إلزامية لأي مطور JavaScript.



1. دوال الاسترجاع (Callback) ومشكلة Callback Hell



تاريخياً، كانت أول آلية للبرمجة غير المتزامنة في JavaScript هي دوال الاسترجاع (callback) — وهي دوال تُستدعى بعد اكتمال العملية غير المتزامنة.



مثال على دالة استرجاع بسيطة



const fs = require('fs');

// قراءة ملف غير متزامنةfs.readFile('example.txt', 'utf8', (err, data) => { if (err) { console.error('خطأ في القراءة:', err); return; } console.log('محتوى الملف:', data);});

console.log('هذه الرسالة ستظهر قبل محتوى الملف');


تظهر المشكلة عندما نحتاج إلى تنفيذ عدة عمليات غير متزامنة متتالية. يزداد تداخل دوال الاسترجاع ويصبح الكود غير قابل للقراءة — هذه الظاهرة تُسمى "Callback Hell" أو "جحيم دوال الاسترجاع".



مثال على Callback Hell



fs.readFile('file1.txt', (err, data1) => {    if (err) throw err;    fs.readFile('file2.txt', (err, data2) => {        if (err) throw err;        fs.readFile('file3.txt', (err, data3) => {            if (err) throw err;            // معالجة الملفات الثلاثة            console.log(data1 + data2 + data3);        });    });});


من الصعب صيانة هذا الكود واختباره وتصحيح أخطائه. جاء حل هذه المشكلة مع ظهور Promise.



2. Promise: التخلص من Callback Hell



Promise (الوعد) هو كائن يمثل نتيجة عملية غير متزامنة: إما تنفيذ ناجح (resolve) أو خطأ (reject). يتيح Promise بناء سلاسل من الاستدعاءات، مما يجعل الكود أكثر تسطحاً وقابلية للقراءة.



إنشاء واستخدام Promise



// دالة تُرجع Promiseconst readFilePromise = (filename) => {    return new Promise((resolve, reject) => {        fs.readFile(filename, 'utf8', (err, data) => {            if (err) reject(err);            else resolve(data);        });    });};

// الاستخدامreadFilePromise('file1.txt') .then(data => { console.log('تم قراءة الملف:', data); return readFilePromise('file2.txt'); }) .then(data => { console.log('الملف الثاني:', data); }) .catch(err => { console.error('خطأ:', err); });


الطرق Promise.all و Promise.race



للتنفيذ المتوازي لعدة عمليات غير متزامنة توجد طرق ثابتة مفيدة:



  • Promise.all() — ينتظر تنفيذ جميع الوعود. إذا انتهى واحد على الأقل بخطأ، يتحول Promise.all بالكامل إلى حالة rejected.
  • Promise.race() — يُرجع نتيجة أول وعد مكتمل (نجاح أو خطأ).
  • Promise.allSettled() — ينتظر اكتمال جميع الوعود، ويُعيد نتائج كل منها (نجاح أو خطأ).


const promise1 = readFilePromise('file1.txt');const promise2 = readFilePromise('file2.txt');const promise3 = readFilePromise('file3.txt');

Promise.all([promise1, promise2, promise3]) .then(results => { console.log('تم قراءة جميع الملفات:', results); }) .catch(err

المدونات

توصيات الكتب