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

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

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

ابدأ الدورة

البرمجة غير المتزامنة في JavaScript: من دوال الاسترجاع إلى async/await



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



1. دوال الاسترجاع (Callbacks): أساس عدم التزامن



دالة الاسترجاع (Callback) هي دالة تُمرر إلى دالة أخرى كوسيط (argument) ويتم استدعاؤها بعد اكتمال العملية غير المتزامنة. هذا هو النمط الأقدم والأساسي في JavaScript. في Node.js، تستخدم العديد من الوحدات المدمجة (fs, http) دوال الاسترجاع.



مثال على قراءة ملف باستخدام دالة استرجاع:



const fs = require('fs');

console.log('نبدأ قراءة الملف...');

fs.readFile('example.txt', 'utf8', (err, data) => { if (err) { console.error('خطأ:', err); return; } console.log('محتوى الملف:', data);});

console.log('الكود يستمر في التنفيذ...');


سيظهر الإخراج في وحدة التحكم أن "الكود يستمر في التنفيذ..." يظهر قبل محتوى الملف. هذا هو السلوك غير الحاجز (non-blocking): الخيط الرئيسي لا ينتظر اكتمال القراءة، بل يواصل التنفيذ.



مشكلة دوال الاسترجاع: جحيم دوال الاسترجاع (Callback Hell)

عند وجود عمليات غير متزامنة متداخلة، يتحول الكود إلى "هرم جحيمي":



fs.readFile('user.json', (err, userData) => {  if (err) throw err;  const user = JSON.parse(userData);  db.query(`SELECT * FROM posts WHERE user_id=${user.id}`, (err, posts) => {    if (err) throw err;    fs.writeFile('user_posts.json', JSON.stringify(posts), (err) => {      if (err) throw err;      console.log('تم!');    });  });});


من الصعب قراءة هذا الكود وتصحيح أخطائه وصيانته. الحل هو الوعود (Promises).



2. الوعود (Promises): السلاسل ومعالجة الأخطاء



الوعد (Promise) هو كائن يمثل نتيجة مستقبلية لعملية غير متزامنة. يمكن أن يكون في إحدى ثلاث حالات: pending (قيد الانتظار)، fulfilled (تم بنجاح)، أو rejected (مرفوض مع خطأ).



إنشاء وعد بسيط:



const readFilePromise = (filename) => {  return new Promise((resolve, reject) => {    fs.readFile(filename, 'utf8', (err, data) => {      if (err) {        reject(err);      } else {        resolve(data);      }    });  });};

readFilePromise('example.txt') .then(data => console.log('البيانات:', data)) .catch(err => console.error('خطأ:', err));


سلاسل الوعود (Chaining)

الميزة الرئيسية هي القدرة على بناء عمليات متسلسلة دون تداخل:



readFilePromise('user.json')  .then(userData => {    const user = JSON.parse(userData);    return dbQueryPromise(`SELECT * FROM posts WHERE user_id=${user.id}`);  })  .then(posts => {    return writeFilePromise('user_posts.json', JSON.stringify(posts));  })  .then(() => console.log('تم!'))  .catch(err => console.error('خطأ في السلسلة:', err));


التنفيذ المتوازي: Promise.all

عند الحاجة لتنفيذ عدة عمليات مستقلة في وقت واحد:



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 => console.error('أحد الوعود فشل:', err));


ينجح Promise.all فقط إذا تمت جميع الوعود بنجاح. إذا فشل واحد على الأقل، يتم تفعيل

المدونات

توصيات الكتب