ما هي البرمجة غير المتزامنة؟
في Node.js، معظم العمليات تتم بشكل غير متزامن (Asynchronous)، مما يعني أن الكود لا ينتظر انتهاء عملية معينة قبل المتابعة إلى الكود التالي.
لماذا البرمجة غير المتزامنة؟
- عدم حظر التطبيق (Non-blocking)
- أداء أفضل وسرعة استجابة عالية
- التعامل مع عدة طلبات في نفس الوقت
- استخدام أمثل لموارد النظام
الطريقة الأولى: Callbacks
Callbacks هي الطريقة الكلاسيكية للتعامل مع الكود غير المتزامن:
const fs = require('fs');
// قراءة ملف بشكل غير متزامن
fs.readFile('data.txt', 'utf8', (err, data) => {
if (err) {
console.error('حدث خطأ:', err);
return;
}
console.log('محتوى الملف:', data);
});
console.log('هذا السطر سيُطبع قبل قراءة الملف');
مشكلة Callback Hell
عند استخدام عدة Callbacks متداخلة، يصبح الكود صعب القراءة والصيانة (Callback Hell أو Pyramid of Doom)
الطريقة الثانية: Promises
Promises توفر طريقة أفضل وأوضح للتعامل مع الكود غير المتزامن:
const fs = require('fs').promises;
// قراءة ملف باستخدام Promise
fs.readFile('data.txt', 'utf8')
.then(data => {
console.log('محتوى الملف:', data);
return fs.readFile('data2.txt', 'utf8');
})
.then(data2 => {
console.log('محتوى الملف الثاني:', data2);
})
.catch(err => {
console.error('حدث خطأ:', err);
});
// إنشاء Promise مخصص
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
delay(2000)
.then(() => console.log('مرت ثانيتان'));
الطريقة الثالثة: Async/Await (الأفضل!)
async/await هي أحدث وأسهل طريقة للتعامل مع الكود غير المتزامن:
const fs = require('fs').promises;
// استخدام async/await
async function readFiles() {
try {
const data1 = await fs.readFile('data.txt', 'utf8');
console.log('الملف الأول:', data1);
const data2 = await fs.readFile('data2.txt', 'utf8');
console.log('الملف الثاني:', data2);
return 'تمت قراءة جميع الملفات';
} catch (err) {
console.error('حدث خطأ:', err);
throw err;
}
}
readFiles()
.then(result => console.log(result))
.catch(err => console.error(err));
مميزات Async/Await
- كود أسهل في القراءة والفهم
- معالجة أخطاء أبسط باستخدام try/catch
- يشبه الكود المتزامن العادي
- سهولة debugging
مثال عملي: جلب بيانات من API
// تثبيت axios أولاً: pnpm add axios
const axios = require('axios');
// طريقة 1: Promises
function getUserPromise(id) {
return axios.get(`https://jsonplaceholder.typicode.com/users/${id}`)
.then(response => response.data)
.catch(err => {
console.error('خطأ في جلب المستخدم:', err.message);
throw err;
});
}
// طريقة 2: Async/Await (الأفضل)
async function getUser(id) {
try {
const response = await axios.get(
`https://jsonplaceholder.typicode.com/users/${id}`
);
return response.data;
} catch (err) {
console.error('خطأ في جلب المستخدم:', err.message);
throw err;
}
}
// استخدام الدالة
async function main() {
try {
const user = await getUser(1);
console.log('المستخدم:', user.name);
} catch (err) {
console.error('فشل:', err);
}
}
main();
Promise.all للعمليات المتوازية
عندما تريد تنفيذ عدة عمليات غير متزامنة في نفس الوقت:
const axios = require('axios');
async function getMultipleUsers() {
try {
// تنفيذ جميع الطلبات معاً
const results = await Promise.all([
axios.get('https://jsonplaceholder.typicode.com/users/1'),
axios.get('https://jsonplaceholder.typicode.com/users/2'),
axios.get('https://jsonplaceholder.typicode.com/users/3')
]);
const users = results.map(r => r.data);
console.log('المستخدمون:', users);
return users;
} catch (err) {
console.error('خطأ:', err.message);
}
}
getMultipleUsers();
Promise Methods أخرى
Promise.all()- ينتظر جميع Promises (يفشل إذا فشل أي منها)Promise.race()- يعيد أول Promise تنتهيPromise.allSettled()- ينتظر الكل (لا يفشل أبداً)Promise.any()- يعيد أول Promise تنجح