Promise.any

מתודה חדשה ל-Promise מאפשרת ניהול יותר טוב של פרומיסים מרובים.

אחת התוספות הממש חביבות ב-ES2021 הוא ה-any. מתודה שמגיעה עם הפרומיס שבו אנו משתמשים בתכנות אסינכרוני (לא מכירים? זה הזמן! הנה המאמר שלי עליו שכתבתי בתקופה הפרהיסטורית של 2016, אל תשכחו גם את ה-async await!).

אז הנה promise והנה השימוש הבסיסי בו:

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Promise 1'), Math.floor(Math.random() * 1000));
});
p1.then(res => console.log(res)); // Promise 1

אנו כמובן יכולים לה��תמש ב-async await כדי להשתמש בפרומיס:

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Promise 1'), Math.floor(Math.random() * 1000));
});

async function callPromise() {
  const res = await p1;
  console.log(res);
};
callPromise(); // Promise 1

כיוון ש-async await זו הדרך המודרנית, אני אשתמש בה בהמשך המאמר. אני רק מזכיר שמדובר בפיצ'ר של ES2017 אז אם אתם לא מכירים, זו ההזדמנות להכיר.

טוב, הכל מעולה, אבל מה קורה אם יש לנו כמה פרומיסים? איך אני מנהל אותם? אני יכול לקרוא לאחד אחרי השני.

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Promise 1'), Math.floor(Math.random() * 1000));
});
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Promise 2'), Math.floor(Math.random() * 1000));
});
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Promise 3'), Math.floor(Math.random() * 1000));
});

async function callPromise() {
  let res = await p1;
  console.log(res);
  res = await p2;
  console.log(res);
  res = await p3;
  console.log(res);
};
callPromise(); // Promise 1, Promise 2, Promise 3

בעוד שמדובר בדרך לגיטימית, זו דרך מאוד לא יעילה לעבוד בה אם אני רוצה לשגר את כל הפרומיסים ביחד. פה אני קורא ל-p1, מחכה שהוא יסתיים, קורא ל-p2, מחכה שהוא יסתיים, קורא ל-p3 ומחכה שהוא יסתיים וכך הלאה. אם אחת מהן נכשלת, אז השניים האחרות לא יצאו. זה לא ממש מוצלח אם שאר הקריאות לא תלויות זו בזו.

אם אני רוצה שכולן יצאו בבת אחת אני אשתמש ב-all. כך:

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Promise 1'), Math.floor(Math.random() * 1000));
});
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Promise 2'), Math.floor(Math.random() * 1000));
});
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Promise 3'), Math.floor(Math.random() * 1000));
});

async function callPromise() {
  let res = await Promise.all([p1,p2,p3]);
  console.log(res);
};
callPromise(); // ["Promise 1", "Promise 2", "Promise 3"]

זה מנוהל יותר טוב, אבל אם פרומיס אחד נכשל? הכל נכשל. לא נקבל תוצאה אחת אפילו אלא error. זה יכול להיות סבבה אם אני רוצה שכולן יצליחו. אבל מה אם אני רוצה שכל הפרומיסים יישלחו ואני לא רוצה שגיאה אם הם ייכשלו? אז יש לי את allSettled שעליו גם כתבתי בעבר – הוא יצא בשנה הקודמת:

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Promise 1'), Math.floor(Math.random() * 1000));
});
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Promise 2'), Math.floor(Math.random() * 1000));
});
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Promise 3'), Math.floor(Math.random() * 1000));
});

async function callPromise() {
  let res = await Promise.allSettled([p1,p2,p3]);
  console.log(res);
};
callPromise(); 
/*
[{status: "fulfilled", value: "Promise 1"},
{status: "fulfilled", value: "Promise 2"},
{status: "fulfilled", value: "Promise 3"},
*/

יש לנו גם את race. שהוא מחזיר את הפרומיס שחזרה הכי מהר בלי קשר להצלחה או לכשלון – מה זאת אומרת? זה אומרת שאם פרומיס אחד עושה reject, אנו מקבלים error.

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Promise 1'), Math.floor(Math.random() * 1000));
});
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Promise 2'), Math.floor(Math.random() * 1000));
});
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Promise 3'), Math.floor(Math.random() * 1000));
});

async function callPromise() {
  let res = await Promise.race([p1,p2,p3]);
  console.log(res);
};
callPromise(); // Maybe 1, 2 or 3

וזה? זה יכול לבאס מאוד. כי אם אני רוצה את הפרומיס הראשון שמצליח – לא זה שמחזיר לי תשובה, זה עלול להיות סופר בעייתי. בדיוק בשביל זה יש לי את any. הוא עובד בול כמו race, אבל רק על פרומיסים שמצליחים!

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Promise 1'), Math.floor(Math.random() * 1000));
});
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Promise 2'), Math.floor(Math.random() * 1000));
});
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Promise 3'), Math.floor(Math.random() * 1000));
});

async function callPromise() {
  let res = await Promise.any([p1,p2,p3]);
  console.log(res);
};
callPromise(); // Maybe 1, 2 or 3

אם יש אחד שייכשל? ה-any לא יזרוק שגיאה, הוא פשוט יתעלם ויחכה לפרומיס הבא שיצליח.

const p1 = new Promise((resolve, reject) => reject('Promise 1 fail'));
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Promise 2'), Math.floor(Math.random() * 1000));
});
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Promise 3'), Math.floor(Math.random() * 1000));
});

async function callPromise() {
  let res = await Promise.any([p1,p2,p3]);
  console.log(res);
};
callPromise(); // Maybe 2 or 3, never 1!

ומה קורה עם כולם נכשלים? אז פה יש לנו שגיאה.

const p1 = new Promise((resolve, reject) => reject('Promise 1 fail'));
const p2 = new Promise((resolve, reject) => reject('Promise 2 fail'));
const p3 = new Promise((resolve, reject) => reject('Promise 3 fail'));

async function callPromise() {
  try {
    let res = await Promise.any([p1,p2,p3]);
    console.log(res);
  } catch(e) {
    console.log(e); 
  }
};

callPromise(); // AggregateError: All promises were rejected

מדובר בעוד כלי בארסנל של הפרומיסים שמאפשר לנהל פרומיסים מרובים בהתאם לצורך ומאפשר גמישות גדולה ��ם קוד אלגנטי. יופי של תוספת ל-2021.

פוסטים נוספים שכדאי לקרוא

יסודות בתכנות

איך TCP עובד? מבט מעמיק

הסבר מעמיק מתחת למנוע על איך תקשורת TCP עובדת כולל ניתוח פקטות.

ממנטו עם גמל אין לי מושג למה אני עושה דברים
יסודות בתכנות

מה זה stateful ו-stateless

מה זה סטייט? למה אנחנו צריכים לדעת מה זה ואיך רואים את זה ממש בעיניים.

יסודות בתכנות

Decoupling ו-Coupling בהנדסת תוכנה

הסבר על מושג מרכזי בהנדסת תוכנה ובכתיבת קוד שכדאי להכיר במיוחד כשמנחים LLM בכתיבת קוד.

פתרונות ומאמרים על פיתוח אינטרנט

העולם המדהים של Chrome debugging

איך תוכנות שונות מפעילות את כרום כרצונן? איך דיבאגר בפרונט עובד? צלילה לעומק לתוך העולם המופלא של CDP

גלילה לראש העמוד