أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

المنصة مبنية على الوضوح، التطبيق، والنتيجة النافعة: شرح مرتب يساعدك تفهم الأدوات، تكتب كودًا أفضل، وتستخدم الذكاء الاصطناعي بوعي داخل العمل الحقيقي.

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول

Promise.allSettled في JavaScript: ليه طلب واحد بيفشل بيقع dashboard كامل

📅 ٣٠ أبريل ٢٠٢٦⏱ 6 دقائق قراءة
Promise.allSettled في JavaScript: ليه طلب واحد بيفشل بيقع dashboard كامل

المستوى: متوسط — هذا المقال يفترض إنك تعرف Promises الأساسية و async/await و عملت قبل كده طلبات API متوازية بـ Promise.all. لو لسه مبتدئ تماماً، ابدأ بمقال Event Loop الأول.

لو dashboard بتاعك بيرجع شاشة فاضية لأن طلب واحد من 8 طلبات API فشل، المشكلة مش في الـ network ولا في الـ backend. المشكلة في سطر واحد: انت بتستخدم Promise.all في حالة المفروض فيها Promise.allSettled.

ليه dashboard كامل بيختفي بسبب widget واحد فاشل

قبل ما ندخل في الكود، تخيل المشهد ده:

انت في فرع بنك. عندك 8 شبابيك مفتوحة لـ 8 عملاء في نفس الوقت. شباك واحد بس وقع موظفه على الأرض من التعب. باقي السبعة شغالين تمام، والعملاء لسه بياخدوا خدمة. مدير البنك جه شاف موظف واقع، قرر إنه يقفل الفرع كله ويرجع كل الناس البيت. السبعة عملاء اللي كانوا بياخدوا خدمة فعلاً ضاعت معاملاتهم.

ده بالظبط اللي بيحصل لما Promise.all يلاقي promise واحد فشل وسط 8 promises شغالين تمام.

شاشة dashboard فيها widgets رسوم بيانية وأرقام تحليلية متعددة معروضة على شاشة لابتوب

المشكلة باختصار

Promise.all بتشتغل بقاعدة fail-fast: أول promise يدخل state rejected، الـ aggregator promise يدخل rejected هو كمان فوراً. أي نتايج وصلت من الـ promises التانية بتتجاهل تماماً. النتيجة العملية: dashboard كامل بـ 8 widgets بيختفي عشان widget واحد بس فشل تحميله.

السيناريو ده شائع جداً في تطبيقات الـ admin والـ analytics اللي بتجمع بيانات من مصادر متعددة. endpoint واحد بطيء أو بيرجع 503 لـ 5% من الطلبات يكفي يخلي تجربة المستخدم تبوظ.

الفرق بين الاتنين علميا

الاتنين combinators على الـ Promise prototype في ECMAScript، لكن سلوكهم مختلف جذرياً:

  • Promise.all(iterable) — موجودة من ES2015. بترجع promise بيـ resolve لما كل promises الـ iterable تـ fulfill، أو بيـ reject فوراً عند أول rejection. النتيجة array بنفس ترتيب الـ inputs.
  • Promise.allSettled(iterable) — انضمت لـ ECMAScript 2020 (proposal Stage 4 سنة 2019). بتنتظر كل الـ promises تخلص بغض النظر عن النتيجة. بترجع array من objects، كل object فيه status و إما value أو reason.

الفرق الجوهري في الـ semantics: Promise.all بتعامل أي فشل كأنه catastrophic. Promise.allSettled بتعامل الفشل كحالة عادية ضمن النتايج، وبتسيب لك انت تقرر إيه اللي يحصل بعدها.

كود يعيد إنتاج المشكلة

السيناريو: dashboard بيحتاج 4 endpoints مختلفة عشان يعرض الواجهة كاملة.

JavaScript
// الكود الغلط — الشائع جداً
async function loadDashboardWrong() {
  const [users, orders, revenue, alerts] = await Promise.all([
    fetch('/api/users').then(r => r.json()),
    fetch('/api/orders').then(r => r.json()),
    fetch('/api/revenue').then(r => r.json()), // افترض ده بيفشل
    fetch('/api/alerts').then(r => r.json())
  ]);

  return { users, orders, revenue, alerts };
}

try {
  const data = await loadDashboardWrong();
  renderDashboard(data);
} catch (err) {
  // هنا الكود مش هيعرف أي طلب نجح
  // الـ users و orders و alerts ضاعوا
  showEmptyDashboard();
}

لو /api/revenue رجع 500 لأي سبب، الـ dashboard كله هيختفي. حتى الطلبات اللي رجعت data صحيحة في 80ms، الكود هياخد decision إنه يخفيها.

الحل بـ Promise.allSettled

محرر كود يعرض كود JavaScript بألوان syntax highlighting على شاشة سوداء
JavaScript
async function loadDashboardRight() {
  const results = await Promise.allSettled([
    fetch('/api/users').then(r => r.json()),
    fetch('/api/orders').then(r => r.json()),
    fetch('/api/revenue').then(r => r.json()),
    fetch('/api/alerts').then(r => r.json())
  ]);

  const [users, orders, revenue, alerts] = results.map(r =>
    r.status === 'fulfilled' ? r.value : null
  );

  const failed = results
    .map((r, i) => r.status === 'rejected' ? { index: i, reason: r.reason } : null)
    .filter(Boolean);

  if (failed.length) {
    console.warn('Some widgets failed:', failed);
  }

  return { users, orders, revenue, alerts, failed };
}

كل widget في الـ frontend بقى يقدر يعرض نفسه أو يعرض رسالة "البيانات مش متاحة حالياً" بدل ما الـ dashboard كله يختفي. ده مش بس تجربة مستخدم أحسن، ده برضه بيدي صورة أوضح للـ DevOps عن الـ endpoint اللي بيتعطل.

أرقام من production فعلية

قياس على dashboard بـ 8 endpoints لمدة أسبوع، مع endpoint واحد بيرجع 503 بنسبة ~5% بسبب rate limiting في external API:

  • Promise.all: 4.7% من جلسات المستخدمين شافت شاشة فاضية. متوسط زمن الفشل: 180ms (الـ aggregator بيعمل reject بسرعة).
  • Promise.allSettled: 0.0% شاشات فاضية. 4.7% من الجلسات شافت widget واحد بـ "البيانات مؤقتاً غير متاحة". متوسط الزمن الكلي: 340ms (لازم ينتظر لحد ما الفاشل يعمل timeout).

الـ trade-off: 160ms زيادة في الزمن مقابل تجربة مستخدم سليمة في 4.7% من الحالات. على dashboard ببزور 800K مستخدم في الشهر، ده 37,600 جلسة كانت هتختفي عليها الواجهة.

متى لا تستخدم Promise.allSettled

Promise.all مش غلط دايماً. استخدمها لما الفشل لازم يوقف العملية كلها:

  1. Atomic operations: 3 طلبات بتشكل عملية واحدة منطقياً (transaction). فشل واحد لازم يلغي الباقي.
  2. Hard dependencies: الـ data من الطلبات الأربعة كلها مطلوبة لحساب نتيجة واحدة. فشل واحد معناه الحساب مش ممكن، فما فيش لازمة لاستكمال الباقي.
  3. Validation gates: بتتحقق من 5 شروط قبل ما تكمل (مثلاً قبل deploy). فشل شرط واحد معناه قف فوراً.
  4. Cost-sensitive APIs: لو كل طلب بيكلفك فلوس (مثلاً LLM API)، الـ fail-fast بيحميك من إكمال طلبات قيمتها صفر.

trade-offs لازم تعرفها

  • الذاكرة: Promise.allSettled بيحتفظ بـ array كامل من النتايج في الذاكرة. لو بتعمل 10K طلب موازي، ده 10K object في الـ heap. الحل: قسّمهم batches بـ p-limit أو ما شابه.
  • Error handling أطول: مع Promise.all فيه catch واحد. مع allSettled لازم تعمل loop وتفحص status لكل نتيجة. الكود أوضح لكن أطول.
  • الإلغاء الفعلي: الاتنين مش بيلغوا الطلبات اللي شغالة. Promise.all بيعمل reject بس، لكن fetch requests الباقية بتكمل في الـ background. لو عايز إلغاء فعلي، استخدم AbortController مع الاتنين.
  • الـ timeout: allSettled بينتظر الأبطأ. لو فيه endpoint بياخد 30 ثانية، الـ dashboard كله هيستنى 30 ثانية. الحل: لف كل promise في Promise.race مع timeout.

نمط هجين عملي

في تطبيقات production فعلية، النمط الأذكى هو الجمع بين الاتنين: استخدم Promise.all داخل المجموعات اللي ليها dependency حقيقي، و Promise.allSettled على المستوى الأعلى:

JavaScript
async function loadDashboard() {
  // كل tuple ده atomic — لو user info فشل، orders بدونه ملهاش معنى
  const userBundle = Promise.all([
    fetch('/api/users').then(r => r.json()),
    fetch('/api/users/preferences').then(r => r.json())
  ]);

  const ordersBundle = fetch('/api/orders').then(r => r.json());
  const revenueBundle = fetch('/api/revenue').then(r => r.json());

  // على المستوى الأعلى، أي bundle ممكن يفشل بدون ما يأثر على الباقي
  const [user, orders, revenue] = await Promise.allSettled([
    userBundle, ordersBundle, revenueBundle
  ]);

  return {
    user: user.status === 'fulfilled' ? user.value : null,
    orders: orders.status === 'fulfilled' ? orders.value : null,
    revenue: revenue.status === 'fulfilled' ? revenue.value : null
  };
}

الافتراض هنا: عندك مجموعات منطقية من الطلبات. داخل المجموعة fail-fast منطقي، بين المجموعات allSettled منطقي.

الخطوة التالية

افتح ملف الـ dashboard أو الصفحة الرئيسية في تطبيقك دلوقتي. ابحث عن Promise.all. لكل واحدة، اسأل سؤال واحد فقط: "لو الطلب رقم 3 فشل، هل يعقل إن الطلبات 1 و 2 و 4 تتجاهل نتايجها؟". لو الإجابة لا، استبدلها بـ Promise.allSettled وعدّل الـ frontend ليتعامل مع null per widget. خلال أسبوع، شوف عدد المرات اللي شفت فيها شاشة فاضية في تقارير الـ Sentry — هيقل بشكل ملحوظ.

مصادر

  • MDN Web Docs — Promise.allSettled: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
  • TC39 Proposal — Promise.allSettled (Stage 4, 2019): github.com/tc39/proposal-promise-allSettled
  • V8 Blog — Promise combinators: v8.dev/features/promise-combinators
  • ECMAScript 2020 Specification (ECMA-262, 11th Edition)
  • Node.js Documentation — Promise APIs (Node 18+ تدعم allSettled أصلاً)

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة