הבנת Promise ב-JavaScript

מחבר:
בתאריך:

Promise הוא אובייקט של JavaScript שבתוכו אנחנו שמים קוד, שמחזיר אחת משתי תוצאות, הצלחה או כישלון.

 

התחביר של Promise

האובייקט Promise מקבל פונקציה בתור פרמטר, והפונקציה הזו מקבלת שני פרמטרים: resolve ו-reject.

new Promise(function(resolve, reject) {});

ובתחביר מודרני:

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

האובייקט Promise מחזיר אחת מ- 2 מתודות:

  • resolve - שתקרא במקרה שהקוד הצליח.
  • reject - שתקרא במידה והקוד נכשל.

בואו נדגים באמצעות הטלת מטבע:

let flipCoin = new Promise((resolve, reject) => {
  // The code     
  let head = (Math.random() < 0.5);
  
  // Resolve in the case of success
  if(head === true){
    resolve("Yeah, it's head!");
  }

  // Otherwise reject
  reject("No luck this time!");
});

הקוד משתמש בפונקציה rand של JavaScript כדי להציב ערך אקראי למשתנה head, ובמקרה שהערך של head הוא true ה-Promise קורא למתודה resolve, אחרת נקראת המתודה reject.

אם תריצו את הקוד ייתכן שתתאכזבו כי לא תקבלו תוצאה, וזה מפני שאנחנו צריכים לקרוא ל-Promise, וגם לקלוט את התגובה באמצעות המתודות then ו-catch. המתודה then קולטת את ה-resolve, והמתודה catch קולטת את ה-reject.

כך זה נראה:

flipCoin
    .then(fromResolve => console.log(fromResolve))
    .catch(fromReject => console.log(fromReject));

בחיי היום-יום אנחנו משתמשים ב- promise עבור קוד א-סינכרוני.

בחיי היום-יום אנחנו משתמשים ב- promise עבור קוד א-סינכרוני. לדוגמה, קריאה ב-ajax ל-API חיצוני או טעינת תמונה ל-DOM. נחקה את זה באמצעות setTimeout שישהה את התגובה.

let flipCoin = new Promise((resolve, reject) => {
  let head = (Math.random() < 0.5);
  window.setTimeout(() => {
    if(head === true){
      resolve("Yeah, it's a head!");
    }
    reject("No luck this time!");
  }, 1000);
});

flipCoin
    .then(fromResolve => console.log(fromResolve))
    .catch(fromReject => console.log(fromReject));

וזו התוצאה:

מדריך promise של js

* האנימציה ממחישה את ההשהיה עד לקבלת התוצאה.

אז מה היה לנו עד עכשיו? תחביר אובייקט ה-Promise עם המתודות reolve ו-reject, והקוד שקולט את התוצאה של ה-promise באמצעות המתודות then ו-catch.

 

שרשור מתודות

השימוש בתחביר ה-promise הוא מועיל במיוחד במקרים שבהם אנחנו צריכים לחכות שפונקציה אחת תסיים כדי שנוכל להריץ את הפונקציה הבאה בתור. ב-JavaScript קלסי השגנו את המטרה הזו על ידי הכנסת הפונקציות אחת לתוך השנייה. מה שגרם למקרים קשים של "עצי חג מולד מהגיהינום". בואו נדגים את זה באמצעות שלושת השלבים של עבודה על פרויקט בשביל לקוח.

talkWithTheCustomer((input) => {
    workOnTheProject((result) => {
        deliverOnTime((finalResult) => {
            console.log('Finito!');
        }, failureCallback);
    }, failureCallback);
}, failureCallback);

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

אבל עדיף קוד מודולרי כי הוא קריא יותר וקל יותר לתחזוקה, ואת זה נוכל לעשות באמצעות תחביר ה-Promise:

talkWithTheCustomer(input)
  .then(result      => workOnTheProject(result))
  .then(result      => deliverOnTime(result))
  .then(finalResult => console.log('Finito!'))
  .catch(fromReject => console.log('Error'));

כדי שהקוד יעבוד נתחיל בכתיבת ההבטחות.

הפונקציה talkWithTheCustomer עוטפת את ה-Promise הראשון שיצליח רק במקרה שהערך של input הוא "agree".

let talkWithTheCustomer = (input) => {
  return new Promise((resolve, reject) => {
    window.setTimeout(() => {
      if(input === 'agree') {
        resolve("OK");
      }
      reject("Sorry, non starter");
    }, Math.random()*1000);
  });
}

שימו לב, חובה להחזיר את ה-Promise באמצעות return אחרת זה לא יעבוד.

עוד טריק קטן שהשתמשתי בו הוא שזמן ההשהיה באמצעות setTimeout נקבע באקראי כדי לחקות את הזמן הלא ידוע עד לקבלת תגובה לקוד א-סינכרוני (לדוגמה, כזה שמגיע מ-API שיושב על שרת מרוחק).

ההבטחה workOnTheProject תצליח במידה והערך של everythingWorked הוא true.

let workOnTheProject = (result) => {
  return new Promise((resolve, reject) => {
    window.setTimeout(() => {
      let everythingWorked = true;
      if(everythingWorked) {
        resolve(result + " >> Everything works according to plan");
      }
      reject("Sorry, can't deliver on time");
    }, Math.random()*2000);
  });
}

כדי לראות את התוצאה, צריך לקרוא ל-promise:

let input = "agree";
talkWithTheCustomer(input)
  .then(result      => workOnTheProject(result))
  .then(finalResult => console.log(`${finalResult} >> The project was delivered!`))
  .catch(fromReject => console.log(`Problem: ${fromReject}`)); //

כל עוד ה-promises מתקיימים, הקוד ממשיך ועובר מפונקציה לפונקציה לפי הסדר של השרשור עד לסיום, אבל במקרה שאחת ההבטחות כושלת, הקוד מפסיק, וקופץ למתודה catch שתופסת פיקוד. כדי לראות כיצד זה פועל נסו לשנות את הערך של המשתנה input , ואז תראו שה-catch הוא זה שמגיב עם ההודעה שמספק ה- reject של הפונקציה הראשונה, והפונקציה השנייה בכלל לא רצה.

 

המתודות all ו-race

אם אנחנו רוצים שכל הפונקציות יסיימו ורק אז להריץ קוד אז נשתמש במתודה all של האובייקט Promise. all מקבלת בתור פרמטר את מערך הפונקציות, ורק בסוף נשרשר את הקוד שצריך לרוץ בתגובה לכך שמערך הפונקציות סיים לרוץ.

Promise
  .all([a(), b(), c()])
  .then(()=>console.log("All the functions have finished"));

אם אנחנו יכולים להסתפק בזה שרק אחת הפונקציות תסיים לרוץ כדי להריץ את הקוד, אז נשתמש במתודה race של האובייקט Promise.

Promise
  .race([a(), b(), c()])
  .then(()=>console.log("One of the functions has finished"));

race שימושי כאשר פונים למספר API חיצוניים שעושים את אותה העבודה, ומספיק שאחד מהם מספק את המידע כדי שנוכל להריץ את הקוד שלנו.

מדריך JavaScript למתקדמים

 

אהבתם? לא אהבתם? דרגו!

0 הצבעות, ממוצע 0 מתוך 5 כוכבים

 

הוסף תגובה חדשה

 

= 7 + 8

תמונת המגיב

איתי בתאריך: 31.03.2018

דוגמה מצוינת. תודה רבה!

תמונת המגיב

יוסי בן הרוש בתאריך: 31.03.2018

השתדלתי :-)

תמונת המגיב

יצחק בתאריך: 18.10.2018

מאמר מצוין

תמונת המגיב

יוסי בן הרוש בתאריך: 18.10.2018

בשמחה