البرمجة غير المتزامنة هي حجر الزاوية في تطوير الويب الحديث، مما يسمح للتطبيقات بالبقاء سريعة الاستجابة أثناء أداء العمليات التي تستغرق وقتًا طويلاً. تقدم صيغة async/await في JavaScript، التي تم تقديمها في ES2017، طريقة أنظف وأكثر سهولة للتعامل مع الكود غير المتزامن مقارنة بالاستدعاءات التقليدية أو سلاسل الوعد (.then() و .catch()).
فهم Async/Await
async/await هي مجرد سكر نحوي مبني فوق الوعود (Promises). إنها تجعل الكود غير المتزامن يبدو وكأنه كود متزامن، مما يجعله أسهل في القراءة والفهم.
الدوال غير المتزامنة (Async Functions):
يتم الإعلان عن دالة async باستخدام الكلمة الرئيسية async. إنها تعيد دائمًا وعدًا. إذا أعادت الدالة قيمة، فسيتم حل الوعد بهذه القيمة. إذا ألقت الدالة خطأ، فسيتم رفض الوعد بهذا الخطأ.
الكلمة الرئيسية Await:
لا يمكن استخدام الكلمة الرئيسية await إلا داخل دالة async. إنها تخبر الدالة بإيقاف التنفيذ مؤقتًا وانتظار حل الوعد قبل الانتقال إلى السطر التالي من الكود. أثناء الانتظار، يمكن تشغيل نصوص برمجية أخرى، مما يحافظ على استجابة تطبيقك.
الاستخدام الأساسي
إليك مثال بسيط على استخدام async/await لجلب البيانات من واجهة برمجة تطبيقات (API):
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Fetch error:', error);
}
}
fetchData();
في هذا المثال:
- تم الإعلان عن دالة
fetchDataكـasync، مما يسمح باستخدامawaitبداخلها. - توقف الكلمة الرئيسية
awaitتنفيذ الدالة حتى يتم حل وعدfetchبكائنResponse. - نتحقق مما إذا كانت الاستجابة ناجحة. إذا لم تكن كذلك، فإننا نلقي خطأ سيتم التقاطه بواسطة كتلة
catch. - يتم استخدام الكلمة الرئيسية
awaitمرة أخرى لانتظار حل وعدresponse.json()مع البيانات المحللة. - يتم التقاط وتسجيل أي أخطاء أثناء عملية الجلب أو التحليل في كتلة
catch.
معالجة الأخطاء باستخدام Try...Catch
يسمح استخدام كتل try...catch مع async/await بمعالجة أخطاء مباشرة بأسلوب متزامن، وهو ما يجده العديد من المطورين أنظف من كتل .catch() مع الوعود.
async function getUserData(userId) {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error('User not found');
}
const user = await response.json();
return user;
} catch (error) {
console.error('Error fetching user data:', error);
throw error; // Re-throwing the error for further handling by the caller
}
}
التنفيذ المتوازي مع Promise.all
عندما يكون لديك عدة عمليات غير متزامنة مستقلة، يمكنك تنفيذها بالتزامن باستخدام Promise.all. هذا أكثر كفاءة بكثير من انتظارها واحدة تلو الأخرى.
async function fetchMultipleData() {
try {
const [data1, data2, data3] = await Promise.all([
fetch('https://api.example.com/data1').then(res => res.json()),
fetch('https://api.example.com/data2').then(res => res.json()),
fetch('https://api.example.com/data3').then(res => res.json())
]);
console.log(data1, data2, data3);
} catch (error) {
console.error('Error fetching data:', error);
}
}
fetchMultipleData();
في هذا المثال، يتم بدء جميع طلبات الجلب الثلاثة في وقت واحد. يعيد Promise.all وعدًا واحدًا يتم حله عند حل جميع الوعود المدخلة. ثم توقف await الدالة مؤقتًا حتى يتم جلب جميع البيانات.
الأخطاء الشائعة التي يجب تجنبها
- نسيان استخدام
await: إذا قمت باستدعاء دالةasyncبدونawait، فستعيد وعدًا معلقًا بدلاً من القيمة المحلولة. - عدم معالجة الأخطاء: قم دائمًا بتغليف استدعاءات
awaitفي كتلtry...catchلمعالجة الرفض المحتمل للوعود. يمكن أن يؤدي رفض الوعود غير المعالج إلى تعطل تطبيقات Node.js. - الاستخدام غير الفعال لـ
awaitداخل الحلقات: سيؤدي استخدامawaitداخل حلقةforإلى التنفيذ التسلسلي، والذي يمكن أن يكون بطيئًا. إذا كانت العمليات لا تعتمد على بعضها البعض، فجمع كل الوعود في مصفوفة واستخدمPromise.allلتشغيلها بالتزامن. - الإفراط في استخدام دوال
async: قم فقط بالإعلان عن الدوال كـasyncعندما تحتاج إلى استخدامawaitبداخلها. يمكن أن يؤدي الإفراط في الاستخدام إلى تعقيد غير ضروري ويجعل من الصعب التفكير في الكود الخاص بك.
من خلال فهم وتنفيذ هذه المفاهيم، يمكنك كتابة كود غير متزامن أنظف وأكثر قابلية للقراءة وأكثر كفاءة في JavaScript، مما يؤدي إلى أداء وصيانة أفضل في تطبيقاتك.