درک Promise.all در JavaScript
ﺯﻣﺎﻥ ﻣﻄﺎﻟﻌﻪ: 7 دقیقه

درک Promise.all در JavaScript

قبل از اینکه promiseها بطور نیتیو در JavaScript معرفی شوند‌، ما برای کدهای آسنکرون(asynchronous) از callbackهای بسیار زیادی استفاده می‌کردیم. البته به طور معمول مشاهده می‌شود از callbackها برای کدهای آسنکرون استفاده می‌شود؛ زیرا بسیاری از توسعه‌دهندگان هنوز فکر می‌کنند که callbackها و promise‌ها هر دو یکسان هستند‌، اما در واقع اینطور نیستند.

در بسیاری از پروژه‌ها استفاده از promise‌ها بجای ‌callbackها برای اجرای کدهای آسنکرون جایگزین و همچنین گزینه اصلی شدند. promise‌ها از برخی جهات به callbackها شباهت دارند، اما سینتکس و درک کد آن‌ها ساده‌تر است.

هنگام کار با callbackها در JavaScript‌، روش‌های زیادی وجود دارد که می‌توانند به کار ما کمک کنند. در این مقاله‌، ما می‌خواهیم روش Promise.all را شرح دهیم.

برای درک نحوه کارکرد Promise.all‌، ابتدا باید نحوه عملکرد promise‌ها را در JavaScript درک کنیم.

promise‌ها

JavaScript تک رشته‌ای است‌، به این معنی که ما فقط می‌توانیم همزمان یک بلاک کد را اجرا کنیم. یعنی کد را به ترتیب اجرا می‌کند و باید قبل از اجرای کد بعدی‌، اجرای کد را به پایان برساند.

یک Promise نشان‌ دهنده نتیجه یک کد‌آسنکرون است. Promise‌ها اغلب برای رسیدگی به کدهای آسنکرون در JavaScript استفاده می‌شود.

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

یک Promise دارای سه حالت احتمالی است:

  • fulfilled (تکمیل)‌: اگر یک Promise تکمیل یابد. دراینصورت promise.then(f) در اسرع وقت f را برمی‌گرداند
  • rejected (شکست)‌: اگر یک Promise رد شود‌، دراینصورت promise.then(undefined, r) در ادامه به سرعت r را برمی‌گرداند
  • pending (انتظار)‌: Promise ای که در صورت رد شدن در انتظار است

برای ایجاد یک Promise‌، ما از کلمه‌ کلیدی new استفاده می‌کنیم‌، و در داخل آبجکت Promise‌، یک تابع را منتقل می‌کنیم. این تابع executor نامیده می‌شود و دو آرگیومنت طول می‌کشد‌، resolve برای نتیجه موفقیت‌آمیز و reject برای نتیجه خطا:

const firstPromise = new Promise((resolve, reject) => { 
  ... 
});

در داخل promise‌، یک شرط وجود دارد و این جایی است که شما منطق کار خود را قرار می‌دهید. در صورت اجرای شرط‌، ما از آرگیومنت resolve برای نتیجه موفقیت‌آمیز استفاده می‌کنیم. در صورت بروز خطا‌ promise‌، آرگیومنت reject را برمی‌گرداند:

const firstPromise = new Promise((resolve, reject) => {
  const sum = () => 1 + 1;
  if (sum() === 2) resolve("Success");
  else reject("Error");
});

promise زنجیره‌ای

Promise های زنجیره‌ای یکی از مواردی است که استفاده از promise‌ها را بسیار عالی و آسان می‌کند. ما می‌توانیم یک زنجیره از کد‌های آسنکرون را اجرا کنیم‌ وبه این ترتیب هر کار به محض اتمام کار قبلی اجرا می‌شود.

ما می‌توانیم promise خود را با استفاده از بلوک then . متصل کنیم‌، هر چیزی که از این بلوک برگردد به یک promise حل شده تبدیل می‌شود:

const firstPromise = new Promise((resolve, reject) => {
  const sum = () => 1 + 1;
  if (sum() === 2) resolve("Success");
  else reject("Error");
});
firstPromise
  .then(success => console.log("success: ", success));

زیبایی کار بلوک then . این است که ما می‌توانیم اقدامات async اضافی را یکی پس از دیگری اجرا کنیم. برای مدیریت خطا‌، می‌توانیم از بلوک catch. استفاده کنیم:

const firstPromise = new Promise((resolve, reject) => {
  const sum = () => 1 + 1;
  if (sum() === 2) resolve("Success");
  else reject("Error");
});
firstPromise
  .then(success => console.log("success: ", success))
  .catch(error => console.log("error: ", error));

با استفاده از callbackها یا promiseها می‌توانید کد‌های آسنکرون را اجرا کنید. اما تفاوت‌هایی وجود دارد.

اگر برای انجام کد‌های آسنکرون از callbackها استفاده می‌کنید‌، در بعضی موارد ممکن است توابع تو‌در‌تو زیادی داشته باشید‌، این همان چیزی است که callback hell نامیده می‌شود. توابع تو‌در‌تو بسیار زیاد باعث می‌شود کد شما ناخوانا و غیرقابل کنترل باشد. با استفاده از promiseها می‌توانید مشکل را حل کنید‌ و کدهای خوانا و قابل کنترل‌تری داشته باشید.

promiseها روشی تمیزتر برای اجرای کد‌های آسنکرون هستند. promiseها مکانیزم catch را آماده می‌کنند، که callbackها آن را ندارند. خلاصه اینکه promiseها کد تمیزتر‌، بهتر و کاربردی را فراهم می‌کنند.

حالا که کمی در مورد promiseها صحبت کردیم‌، بیایید به Promise.all نگاهی بیندازیم.

promise.all

روش Promise.all کد‌های آسنکرون را به سطح کاملاً جدیدی منتقل می‌کند و به ما کمک می‌کند تا گروهی از promiseها را در JavaScript فراهم و اجرا کنیم.

Promise.all مجموعه‌ای از promiseها را به عنوان ورودی دریافت می‌کند. درنظر داشته باشید یا همه promiseها حل می‌شوند یا در صورت رد شدن یکی از promiseها‌، همه آن‌ها رد می‌شوند.

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

در اینجا دو گزینه دارید که می‌توانید برای این مورد استفاده کنید:

  1. شما می‌توانید همه promiseها را یکی یکی اجرا کنید یا آنها را به صورت زنجیره‌ای متصل و داده‌ها را به محض دسترسی پردازش کنید
  2. می‌توانید همه promiseها را به عنوان ورودی آرایه‌ای به Promise.all منتقل کنید و یک مقدار را برگرداند

راه حل بهتر برای استفاده در این مورد استفاده از روش Promise.all است، که همه promiseها را اجرا می‌کند‌، و یک promise  را برمی‌گرداند‌:

const allpromises = Promise.all([Promise1, Promise2, Promise3, Promise4, ...]);

به یاد داشته باشید‌، روش Promise.all تنها در صورت برگشت تمام promiseهای داده شده در آرایه با موفقیت برمی‌گردد. در صورتی که فقط یک promise در آرایه وجود داشته باشد که بازگردانده شود‌، روش Promise.all رد خواهد شد.

به عنوان مثال ، بیایید تصور کنیم که تابعی به نام sum داریم. این تابع فقط مقدار برخی از عملکردها را برای ما برمی‌گرداند:

const sum = (a, b) => a + b;

حال‌، بیایید تصور کنیم که ما پنج promise داریم‌، و در داخل هر یک از این promise‌ها از تابع sum استفاده می‌کنیم و همچنین در داخل یک جمله if‌، مقدار را مقایسه می‌کنیم. در صورت true بودن، ما می‌خواهیم یک پیغام موفقیت‌آمیز ارسال کنیم و در صورت false بودن، یک پیغام خطا ارسال کنیم:

const first = new Promise((resolve, reject) => {
  const value = sum(1, 1);
  if (value === 2) resolve(value);
  else reject(value);
});

const second = new Promise((resolve, reject) => {
  const value = sum(2, 2);
  if (value === 4) resolve(value);
  else reject(value);
});

const third = new Promise((resolve, reject) => {
  const value = sum(3, 3);
  if (value === 6) resolve(value);
  else reject(value);
});

const fourth = new Promise((resolve, reject) => {
  const value = sum(4, 4);
  if (value === 8) resolve(value);
  else reject(value);
});

const fifth = new Promise((resolve, reject) => {
  const value = sum(5, 5);
  if (value === 10) resolve(value);
  else reject(value);
});

برای انجام همزمان همه promiseها، یک ورودی آرایه‌ای‌ را به Promise.all منتقل می‌کنیم:

const allPromises = Promise.all([first, second, third, fourth, fifth]);

اکنون‌، ما فقط promise واحد خود را allpromise می‌نامیم و در نتیجه مجموعه‌ای از مقادیر حل شده را به ما بازمی‌گرداند:

allpromises.then(success => console.log('sucess: ', success)).catch(error => console.log('error: ', error));
// Result
// sucess: [ 2, 4, 2, 8, 10 ]

در صورتی که یکی از promiseها خطایی را برگرداند‌، promise واحد ما نیز خطایی را برمی‌گرداند. در این مثال‌، در promise fifth‌‌، ما می‌خواهیم به عنوان آرگیومنت مقادیر 5 و 6 را به تابع sum را منتقل کنیم.

البته‌، این کد یک خطا را برمی‌گرداند زیرا 5 + 6 مطمئنا 10 نیست. این باعث می‌شود که promise واحد هم خطایی را برگرداند:

const fifth = new Promise((resolve, reject) => {
  const value = sum(5, 6);
  if (value === 10) resolve(value);
  else reject(value);
});

const allpromises = Promise.all([first, second, third, fourth, fifth]);
allpromises.then(success => console.log('sucess: ', success)).catch(error => console.log('error: ', error));

// Result
// error:  11

promise.all در برابر promise.allsettlet

فرض کنید شما promiseهای زیادی دارید که می‌خواهید آنها را اجرا کنید اما اگر بخواهید همه مقادیر را برگردانید‌، بدون توجه به اینکه خطایی در promiseهای شما وجود داشته باشد‌، Promise.all بهترین راه حل برای شما نخواهد بود.

می‌توانید از روش Promise.allSettled برای آن استفاده کنید. این روش یک promise واحد را برمی‌گرداند که پس از تایید یا رد همه promiseها حل خواهد شد.

بیایید به عنوان آخرین مثال خود از آن استفاده کنیم یعنی به جای استفاده از روش Promise.all‌، از روش Promise.allSetelled استفاده کنیم:

const allpromises = Promise.allSettled([first, second, third, fourth, fifth]);
allpromises.then(success => console.log('sucess: ', success)).catch(error => console.log('error: ', error));

// Result
// success:  [
//   { status: 'fulfilled', value: 2 },
//   { status: 'fulfilled', value: 4 },
//   { status: 'fulfilled', value: 6 },
//   { status: 'fulfilled', value: 8 },
//   { status: 'rejected', reason: 11 }
// ]

چه موقع باید استفاده کرد

برای استفاده از روش Promise.all‌، ابتدا باید بدانید که برای انجام رساندن کار به چه چیزی نیاز دارید. Promise.allmethod در بعضی موارد بسیار مفید است‌، مثلا:

  1. کارهایی که انجام می‌دهید به یکدیگر وابسته هستند و می‌خواهید بدانید که آیا همه Promise‌ها با موفقیت به پایان رسیده اند یا خیر
  2. باید از API های مختلف درخواست داشته باشید و پس از تمام واکنش‌ها‌‌، می‌خواهید کاری را با نتیجه انجام دهید

Promise.all یک روش عالی برای دستیابی به همزمانی در JavaScript است‌، هنگامی که چندین Promise‌ دارید و می‌خواهید همه آنها را اجرا کنید، یکی از بهترین روش‌ها برای اجرا همزمان کدهای آسنکرون در JavaScript است.

نتیجه

در این مقاله‌، ما کمی در مورد Promise‌ها در JavaScript صحبت کردیم و درباره روش Promise به نام Promise.all اطلاعات بیشتری کسب کردیم. این روش یک روش بسیار مفید و کاربردی برای جمع آوری و انجام بسیاری از Promise‌ها و برگرداندن یک Promise واحد با تمام مقادیر داخل یک آرایه است.

منبع

چه امتیازی برای این مقاله میدهید؟

خیلی بد
بد
متوسط
خوب
عالی
5 از 1 رای

/@gholamuuuu
جواد غلامی
طراح و توسعه دهنده وب

جواد هستم طراح و توسعه دهنده وب. مهندسی برق می خونم

دیدگاه و پرسش

برای ارسال دیدگاه لازم است وارد شده یا ثبت‌نام کنید ورود یا ثبت‌نام

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

جواد غلامی

طراح و توسعه دهنده وب

مقالات برگزیده

مقالات برگزیده را از این قسمت میتوانید ببینید

مشاهده همه مقالات