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.

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

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

SSG עם next

אחרי שלמדנו במאמר הקודם מה זה SSR והבנו שלא מדובר בקליע כסף שפותר את כל הבעיות שלנו, נלמד על SSG שיכול להקל על כמה מהבעיות של SSR.

פייתון

קבצי קונפיגורציה בפואטרי

הגדרות שמשנות את ההתנהגות של פואטרי באמצעות קבצי הגדרות גלובליים, לוקליים ואפילו משתני סביבה.

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

נגישות טכנית – פודקאסט ומבוא

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

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