لو عندك دالة بتعمل 5 طلبات HTTP متوازية، واحد منهم فشل، الـ function كلها بترمي خطأ وكل النتائج التانية بتضيع. ده مش bug في الكود، ده اختيار غلط بين Promise.all و Promise.allSettled.
Promise.all ضد Promise.allSettled: الفرق بالظبط
المشكلة باختصار
الاتنين بياخدوا array من promises ويشغّلوهم بالتوازي. الفرق الحقيقي في سياسة الفشل: Promise.all بترفض فورًا أول ما أي promise يفشل، وبترجع reason بتاع أول فشل بس. Promise.allSettled بتستنى الكل ينتهي، وبترجع array فيه نتيجة كل promise سواء نجح أو فشل. الفرق ده بيحدد هل المستخدم هيشوف data ناقصة ولا شاشة فاضية.
اللي بيحصل فعلاً في كل واحد
Promise.all بتتصرف fail-fast: أول rejection بيحوّل الـ promise الراجعة لحالة rejected، والباقي لسه شغّال لكن نتيجته بترمى. ركز: الطلبات الباقية مش بتتلغى تلقائيًا، الـ network request لسه ماشي، إنت بس مش هتشوف نتيجته. لو عايز تلغي فعلاً، محتاج AbortController يدوي.
Promise.allSettled بتستنى كل promise يوصل لحالة settled (fulfilled أو rejected). بترجع array من objects، كل object فيه status و value أو reason:
[
{ status: "fulfilled", value: { id: 1 } },
{ status: "rejected", reason: Error("timeout") },
{ status: "fulfilled", value: { id: 3 } }
]
سيناريو واقعي بيحصل كل أسبوع
dashboard بيعرض بيانات من 4 مصادر: user profile، notifications، billing، analytics. لو استخدمت Promise.all وخدمة billing وقعت لحظة التحميل، المستخدم شاف شاشة فاضية بدل ما يشوف بياناته الأساسية وplaceholder بسيط مكان الـ billing. ده bug حقيقي بيتكرر في production لأن المبرمج اختار الـ primitive الغلط.
الحل العملي بـ Promise.allSettled:
async function loadDashboard(userId) {
const results = await Promise.allSettled([
fetch(`/api/profile/${userId}`).then(r => r.json()),
fetch(`/api/notifications/${userId}`).then(r => r.json()),
fetch(`/api/billing/${userId}`).then(r => r.json()),
fetch(`/api/analytics/${userId}`).then(r => r.json()),
]);
const pick = (i, fallback) =>
results[i].status === "fulfilled" ? results[i].value : fallback;
return {
profile: pick(0, null),
notifications: pick(1, []),
billing: pick(2, null),
analytics: pick(3, null),
};
}