اجرای موازی چند Promise: Promise.all، allSettled، race و any

تا این‌جا Promiseها را عمدتاً به‌صورت زنجیره‌ای و متوالی دیده‌اید: هر مرحله منتظر پایان مرحله‌ی قبل می‌ماند. اما در بسیاری از موقعیت‌های واقعی، چند عملیات ناهمزمان کاملاً مستقل از هم هستند و نیازی نیست منتظر یکدیگر بمانند؛ مثلاً دریافت هم‌زمان اطلاعات کاربر، سفارش‌ها و اعلان‌ها از سه endpoint متفاوت. جاوااسکریپت برای این نوع سناریوها چهار متد ایستا روی خود شیء Promise ارائه می‌دهد که هرکدام رفتار متفاوتی در برابر موفقیت و شکست دارند.

Promise.all: همه یا هیچ‌کدام

Promise.all آرایه‌ای از Promiseها می‌گیرد و زمانی resolve می‌شود که همه‌ی آن‌ها با موفقیت به پایان برسند. نتیجه، آرایه‌ای از مقادیر است، دقیقاً به همان ترتیبی که Promiseها داده شده‌اند. اما اگر حتی یکی از آن‌ها reject شود، کل Promise.all بلافاصله reject می‌شود، بدون آنکه منتظر بقیه بماند:

اجرای موازی با Promise.all
خروجی کنسول اینجا نمایش داده می‌شود...

از آنجا که هر سه درخواست هم‌زمان و مستقل از هم ارسال می‌شوند، کل زمان اجرا برابر با کندترین درخواست است، نه مجموع زمان هر سه؛ این تفاوت اصلی با نوشتن سه await پشت سر هم است که در آن حالت، درخواست‌ها به‌ترتیب و نه هم‌زمان ارسال می‌شدند.

Promise.allSettled: نتیجه‌ی همه، صرف‌نظر از موفقیت یا شکست

گاهی نیاز است نتیجه‌ی تمام Promiseها بررسی شود، حتی اگر برخی از آن‌ها شکست بخورند. Promise.allSettled برخلاف Promise.all، هرگز زودهنگام reject نمی‌شود؛ بلکه پس از پایان کار تمام Promiseها (چه موفق و چه ناموفق)، آرایه‌ای از نتایج با ساختار مشخص برمی‌گرداند:

Promise.allSettled([userRequest, ordersRequest, notificationsRequest]).then(
  (results) => {
    results.forEach((result) => {
      if (result.status === "fulfilled") {
        console.log("موفق:", result.value);
      } else {
        console.log("ناموفق:", result.reason);
      }
    });
  }
);

این متد برای مواردی مناسب است که شکست یک درخواست نباید مانع نمایش نتایج موفق سایر درخواست‌ها شود؛ مثلاً نمایش داشبوردی که بخشی از آن حتی با خطای یک سرویس، همچنان قابل استفاده باقی بماند.

Promise.race: فقط اولین نتیجه، مهم نیست موفق یا ناموفق

Promise.race به محض settle شدن اولین Promise از آرایه—چه با موفقیت و چه با شکست—بلافاصله همان نتیجه را برمی‌گرداند و منتظر بقیه نمی‌ماند. یکی از کاربردهای رایج آن، پیاده‌سازی محدودیت زمانی (timeout) برای یک درخواست است:

function fetchWithTimeout(url, ms) {
  const timeout = new Promise((_, reject) => {
    setTimeout(() => reject(new Error("درخواست بیش از حد طول کشید")), ms);
  });

  return Promise.race([fetch(url), timeout]);
}

fetchWithTimeout("/api/data", 5000)
  .then((response) => console.log("پاسخ دریافت شد"))
  .catch((error) => console.log(error.message));

در این الگو، اگر درخواست fetch ظرف ۵ ثانیه پاسخ ندهد، Promise مربوط به timeout زودتر settle شده و Promise.race بلافاصله همان خطا را برمی‌گرداند، بدون آنکه منتظر پایان واقعی درخواست شبکه بماند.

Promise.any: اولین موفقیت، نه اولین نتیجه

Promise.any شبیه Promise.race است، با یک تفاوت مهم: فقط به دنبال اولین Promise موفق است. اگر برخی Promiseها زودتر reject شوند، نادیده گرفته می‌شوند و فقط زمانی که همه‌ی آن‌ها شکست بخورند، خودِ Promise.any reject می‌شود:

Promise.any([
  fetch("/api/mirror-1"),
  fetch("/api/mirror-2"),
  fetch("/api/mirror-3"),
])
  .then((firstSuccessful) => {
    console.log("سریع‌ترین سرور موفق پاسخ داد");
  })
  .catch((error) => {
    console.log("هر سه سرور شکست خوردند");
  });

جدول مقایسه‌ی چهار متد

متد زمان resolve شدن زمان reject شدن
Promise.all وقتی همه موفق شوند به محض شکست اولین Promise
Promise.allSettled وقتی همه settle شوند (موفق یا ناموفق) هرگز reject نمی‌شود
Promise.race به محض settle شدن اولین Promise (موفق) به محض settle شدن اولین Promise (ناموفق)
Promise.any به محض اولین موفقیت فقط وقتی همه شکست بخورند

نکات کلیدی

  • Promise.all برای اجرای موازی چند عملیات مستقل که همگی باید موفق شوند مناسب است؛ با اولین شکست، بلافاصله reject می‌شود.
  • Promise.allSettled نتیجه‌ی تمام Promiseها را بدون توجه به موفقیت یا شکست هرکدام برمی‌گرداند.
  • Promise.race فقط به اولین Promise settle‌شده واکنش نشان می‌دهد، چه موفق و چه ناموفق؛ مناسب برای پیاده‌سازی timeout.
  • Promise.any فقط به دنبال اولین موفقیت است و Promiseهای reject‌شده را نادیده می‌گیرد.

جمع‌بندی

چهار متد Promise.all، allSettled، race و any، ابزارهای تخصصی برای مدیریت چند Promise مستقل و هم‌زمان هستند، در حالی که هرکدام رفتاری متفاوت در برابر موفقیت یا شکست دارند. انتخاب درست میان این چهار متد، بسته به نیاز واقعی پروژه—آیا شکست یک بخش باید کل عملیات را متوقف کند یا نه—تأثیر مستقیمی بر پایداری و سرعت برنامه دارد.