أنماط async/await في JavaScript التي يجب معرفتها

Frontend2026-09-20TryQuickToolBox

البرمجة غير المتزامنة هي جزء أساسي من JavaScript الحديثة، وقد جعل async/await الكود أكثر قابلية للقراءة. لكن العديد من المطورين لا يزالون يعانون من الأخطاء الشائعة: رفض الوعود غير المعالج، الطلبات التسلسلية البطيئة، أو معالجة الأخطاء المربكة. في هذه المقالة، سنستكشف أنماط async/await العملية التي يمكنك تطبيقها فورًا لكتابة كود أنظف وأكثر كفاءة.

1. صيغة async/await الأساسية

دالة async تُرجع دائمًا Promise. الكلمة المفتاحية await توقف التنفيذ حتى يتم تسوية Promise، ثم تُرجع قيمتها المحلولة. إذا رُفض Promise، فإنها ترمي خطأ يمكنك التقاطه باستخدام try/catch.

async function fetchData() {
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Fetch failed:', error);
  }
}

هذا هو الأساس. لكن السيناريوهات الواقعية غالبًا ما تتطلب نهجًا أكثر دقة.

2. معالجة الأخطاء بأناقة

واحدة من أكبر مزايا async/await هي أنه يمكنك استخدام try/catch لكل من الأخطاء المتزامنة وغير المتزامنة. ومع ذلك، كن حذرًا مع استدعاءات await المتعددة في كتلة try واحدة: إذا فشل الأول، فلن يتم تنفيذ التالية.

لدقة أفضل، يمكنك تغليف كل await في try/catch خاص به أو استخدام دالة مساعدة لتجنب تكرار كتل try/catch.

async function getUser(id) {
  try {
    const user = await fetchUser(id);
    return user;
  } catch (error) {
    console.error('Failed to fetch user:', error);
    throw error;
  }
}

3. تشغيل الوعود بالتوازي

انتظار عدة وعود مستقلة بالتسلسل هو خطأ شائع في الأداء. إذا كنت بحاجة إلى نتائج من مصادر متعددة، استخدم Promise.all() لتشغيلها بشكل متزامن.

// Sequential: slow
const user = await fetchUser();
const posts = await fetchPosts();

// Parallel: fast
const [user, posts] = await Promise.all([fetchUser(), fetchPosts()]);

استخدم Promise.allSettled() عندما تريد الانتظار حتى يتم تسوية جميع الوعود، بغض النظر عما إذا كانت تُحل أو تُرفض. هذا مفيد عندما تريد جمع النتائج حتى لو فشل بعضها.

4. التنفيذ التسلسلي عندما يكون الترتيب مهمًا

أحيانًا تحتاج إلى معالجة العناصر بالترتيب، مثل عندما تعتمد كل خطوة على الخطوة السابقة. في هذه الحالة، استخدم حلقة for...of مع await بدلاً من forEach (التي لا تنتظر ردود النداء غير المتزامنة).

for (const item of items) {
  await processItem(item);
}

إذا كنت بحاجة إلى تحديد التزامن، ففكر في مكتبات مثل p-limit أو تنفيذ قائمة انتظار بسيطة.

5. تجنب مأزق async/await في الحلقات

استخدام await داخل Array.prototype.forEach هو خطأ كلاسيكي: forEach لا ينتظر رد النداء غير المتزامن، لذا تنتهي الحلقة قبل أن تُحل الوعود. استخدم دائمًا for...of أو map مع Promise.all.

6. async/await مع المهلات الزمنية

لمنع الطلبات المعلقة، يمكنك تسابق وعد مع مهلة زمنية.

function timeout(ms) {
  return new Promise((_, reject) =>
    setTimeout(() => reject(new Error('Timeout')), ms)
  );
}

await Promise.race([fetchData(), timeout(5000)]);

هذا النمط مفيد لاستدعاءات API التي قد تعلق.

7. المولدات غير المتزامنة و for await...of

تسمح لك المولدات غير المتزامنة بإنتاج تدفق من القيم بشكل غير متزامن. يمكنك استهلاكها باستخدام for await...of.

async function* fetchPages() {
  let page = 1;
  while (true) {
    const data = await fetch(`/api/items?page=${page}`);
    if (data.length === 0) break;
    yield data;
    page++;
  }
}

for await (const page of fetchPages()) {
  console.log(page);
}

هذا رائع لواجهات API المقسمة إلى صفحات أو تدفق البيانات.

8. الأخطاء الشائعة وكيفية تجنبها

9. أفضل الممارسات لكتابة كود غير متزامن نظيف

الأسئلة الشائعة

ما الفرق بين async/await و Promises؟

async/await هو سكر نحوي فوق Promises، مما يجعل الكود غير المتزامن يبدو متزامنًا. يبسط التسلسل ومعالجة الأخطاء باستخدام try/catch.

هل يمكنني استخدام await داخل حلقة forEach؟

لا، forEach لا ينتظر ردود النداء غير المتزامنة. استخدم for...of أو Promise.all مع map بدلاً من ذلك.

كيف أشغل عمليات غير متزامنة متعددة بالتوازي؟

استخدم Promise.all() لتشغيلها بشكل متزامن وانتظر حتى يتم حلها جميعًا.

عند العمل مع واجهات JSON API، يمكنك تنسيق والتحقق من الاستجابات بسرعة باستخدام JSON Formatter.