البرمجة غير المتزامنة هي حجر الزاوية في تطوير الويب الحديث، مما يسمح للتطبيقات بالبقاء سريعة الاستجابة أثناء أداء العمليات التي تستغرق وقتًا طويلاً. تقدم صيغة 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، مما يؤدي إلى أداء وصيانة أفضل في تطبيقاتك.