אחת התוספות הממש חביבות ב-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.
2 תגובות
יש לך רעיון ל use case ?
למשל אם יש עניין של performance על המילישניות (דוגמא טובה serverless).
ובמקרה אתה משתמש ב-CDNs, אז כמובן אפשר לבדוק באיזה region נמצא ה-user וככה להחזיק dict של CDNs ע"פ region, ובנוסף אפשר להשתמש גם ב-Promise.Any בשביל לפנות לכל אלו שנמצאים ב-region הנוכחי ולקבל את הראשון שחוזר.
וכן הלאה דוגמאות שונות של performance, אולי lambda vs google function כשאתה מחזיק את שניהם רק בשביל ה-perf ולא אכפת לך אפילו מ-cost, אלא רק שיהיה מהיר.
ובטח שיש דוגמאות הרבה יותר טובות מאלו.