פרק 13: Promises (הבטחות) ב-JavaScript

1. מה זה Promise?

Promise הוא אובייקט שמייצג פעולה אסינכרונית (פעולה שמתבצעת ברקע) והערך שלה עשוי להיות מוכן בעתיד. מדובר במנגנון שמקל על עבודה עם קוד אסינכרוני ומאפשר לכתוב קוד קריא וקל לניהול, במיוחד לעומת השיטה המסורתית של Callbacks.

במילים פשוטות, Promise היא "הבטחה" לערך שיגיע בעתיד. כאשר הפעולה האסינכרונית מסתיימת, ה-Promise יכול להימצא באחת משלוש מצבים:

  1. Pending (בהמתנה): הפעולה האסינכרונית עדיין מתבצעת.
  2. Fulfilled (התקיימה): הפעולה הסתיימה בהצלחה וה-Promise מחזיר תוצאה.
  3. Rejected (נכשלה): הפעולה נכשלה וה-Promise מחזיר שגיאה.

2. יצירת Promise

יצירת Promise נעשית באמצעות המחלקה Promise. פונקציית ה-Promise מקבלת פונקציה אחרת עם שני פרמטרים:

  1. resolve: פונקציה שמופעלת כשהפעולה האסינכרונית הצליחה.
  2. reject: פונקציה שמופעלת כשהפעולה נכשלה.

התחביר:

let promise = new Promise((resolve, reject) => {
    // קוד אסינכרוני
});

דוגמה בסיסית:

let promise = new Promise((resolve, reject) => {
    let success = true;
    
    if (success) {
        resolve("The operation was successful!");
    } else {
        reject("The operation failed.");
    }
});

הסבר:

  • יצרנו Promise שמדמה פעולה אסינכרונית.
  • אם הפעולה מצליחה (במקרה הזה לפי משתנה success), נקרא לפונקציה resolve כדי לציין שהפעולה הסתיימה בהצלחה.
  • אם הפעולה נכשלת, נקרא ל-reject כדי לציין כישלון.

3. שימוש ב-then ו-catch

כאשר פעולה אסינכרונית מסתיימת, אפשר להגיב אליה בעזרת הפונקציות then (אם הצליחה) ו-catch (אם נכשלה).

דוגמה בסיסית עם then ו-catch:

let promise = new Promise((resolve, reject) => {
    let success = true;
    
    if (success) {
        resolve("The operation was successful!");
    } else {
        reject("The operation failed.");
    }
});

promise
    .then(result => {
        console.log(result); // ידפיס את ההודעה מה-resolve אם הצליחה
    })
    .catch(error => {
        console.log(error); // ידפיס את ההודעה מה-reject אם נכשלה
    });

הסבר:

  • אם ה-Promise מתקיים (מתבצע resolve), הפונקציה ב-then תופעל והפלט יהיה "The operation was successful!".
  • אם ה-Promise נדחה (מתבצע reject), הפונקציה ב-catch תופעל והפלט יהיה "The operation failed.".

4. דוגמה עם פעולה אסינכרונית

נבצע דוגמה שמדמה הורדה של קובץ – פעולה שאורכת זמן, ולכן היא אסינכרונית.

דוגמה:

function downloadFile() {
    return new Promise((resolve, reject) => {
        console.log("Starting file download...");
        
        setTimeout(() => {
            let success = true; // נניח שההורדה הצליחה
            
            if (success) {
                resolve("File downloaded successfully.");
            } else {
                reject("File download failed.");
            }
        }, 3000); // סימולציה של 3 שניות להורדה
    });
}

downloadFile()
    .then(result => {
        console.log(result); // מציג את ההודעה אם ההורדה הצליחה
    })
    .catch(error => {
        console.log(error); // מציג את ההודעה אם ההורדה נכשלה
    });

הסבר:

  • הפונקציה downloadFile מחזירה Promise שמדמה הורדת קובץ.
  • אחרי 3 שניות, אם ההורדה "מצליחה", אנחנו מפעילים את resolve עם הודעת הצלחה.
  • אם ההורדה נכשלת, אנחנו מפעילים את reject עם הודעת כישלון.
  • הקריאה ל-downloadFile() מחזירה Promise ואנחנו משתמשים ב-then כדי להגיב להצלחה וב-catch כדי להגיב לכישלון.

5. עבודה עם Promises מרובים – Promise.all

אפשר להפעיל כמה Promises במקביל ולהמתין עד שכולם יסתיימו באמצעות הפונקציה Promise.all. הפונקציה מקבלת מערך של Promises ומחזירה Promise חדש שנפתר כאשר כל ה-Promises במערך התקיימו, או שנכשל אם אחד מהם נדחה.

דוגמה ל-Promise.all:

let promise1 = new Promise((resolve, reject) => {
    setTimeout(() => resolve("Promise 1 completed!"), 2000);
});

let promise2 = new Promise((resolve, reject) => {
    setTimeout(() => resolve("Promise 2 completed!"), 1000);
});

Promise.all([promise1, promise2])
    .then(results => {
        console.log(results); // מציג מערך עם כל התוצאות ["Promise 1 completed!", "Promise 2 completed!"]
    })
    .catch(error => {
        console.log(error); // אם אחד ה-promises נכשלים
    });

הסבר:

  • שני ה-Promises יתחילו לפעול במקביל: הראשון יסתיים אחרי 2 שניות והשני אחרי 1 שנייה.
  • Promise.all ימתין עד ששני ה-Promises יסתיימו, ורק אז יקרא ל-then עם מערך התוצאות.
  • אם אחד מה-Promises היה נדחה, catch היה מופעל מיד.

6. עבודה עם Promise.race

במקרה שתרצה לקבל תוצאה ברגע שה-Promise הראשון מסתיים, אפשר להשתמש ב-Promise.race. הפונקציה תחזיר את ה-Promise הראשון שמסתיים, לא משנה אם הוא הצלחה או כישלון.

דוגמה ל-Promise.race:

let promise1 = new Promise((resolve, reject) => {
    setTimeout(() => resolve("Promise 1 completed!"), 2000);
});

let promise2 = new Promise((resolve, reject) => {
    setTimeout(() => resolve("Promise 2 completed!"), 1000);
});

Promise.race([promise1, promise2])
    .then(result => {
        console.log(result); // יציג את התוצאה של ה-Promise שהסתיים ראשון ("Promise 2 completed!")
    })
    .catch(error => {
        console.log(error);
    });

הסבר:

  • Promise.race יחזיר את התוצאה של ה-Promise הראשון שמסתיים.
  • בדוגמה זו, promise2 יסתיים ראשון ולכן התוצאה שלו תודפס.

7. Chaining (שרשור) של Promises

אפשר לשרשר פעולות על בסיס תוצאות של Promises קודמים, כלומר להפעיל סדרת פעולות שמבוססות על התוצאה של הפעולה הקודמת.

דוגמה ל-Chaining:

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

promise
    .then(result => {
        console.log(result); // מציג 5
        return result * 2;
    })
    .then(result => {
        console.log(result); // מציג 10
        return result * 3;
    })
    .then(result => {
        console.log(result); // מציג 30
    })
    .catch(error => {
        console.log(error);
    });

הסבר:

  • כל then מקבל את התוצאה של ה-Promise הקודם, מבצע פעולה, ומחזיר ערך חדש להמשך השרשרת.
  • הפלט של הדוגמה יהיה: 5, 10, 30.

8. סיכום מצב עם finally

הפונקציה finally מאפשרת להריץ קוד שיבוצע תמיד לאחר שה-Promise הסתיים, בין אם הוא התקיים ובין אם נדחה.

דוגמה עם finally:

let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("Operation completed"), 2000);
});

promise
    .then(result => {
        console.log(result); // מציג "Operation completed"
    })
    .catch(error => {
        console.log(error);
    })
    .finally(() => {
        console.log("Cleanup complete.");
    });

הסבר:

  • הפונקציה finally תופעל תמיד בסוף התהליך, גם אם היה resolve וגם אם היה reject.
  • בדוגמה זו, הודעת ה-Cleanup תודפס בכל מקרה לאחר סיום ה-Promise.

דוגמה מלאה: תהליך אסינכרוני עם Promises

נבנה דוגמה שמדמה תהליך של בדיקת קובץ, הורדתו ועיבודו, תוך שימוש בשרשור של Promises.

function checkFile() {
    return new Promise((resolve, reject) => {
        console.log("Checking file...");
        setTimeout(() => resolve("File exists"), 1000);
    });
}

function downloadFile() {
    return new Promise((resolve, reject) => {
        console.log("Downloading file...");
        setTimeout(() => resolve("File downloaded"), 2000);
    });
}

function processFile() {
    return new Promise((resolve, reject) => {
        console.log("Processing file...");
        setTimeout(() => resolve("File processed"), 1500);
    });
}

checkFile()
    .then(result => {
        console.log(result); // File exists
        return downloadFile();
    })
    .then(result => {
        console.log(result); // File downloaded
        return processFile();
    })
    .then(result => {
        console.log(result); // File processed
    })
    .catch(error => {
        console.log("Error:", error);
    })
    .finally(() => {
        console.log("Process completed.");
    });

הסבר:

  • הפונקציות checkFile, downloadFile ו-processFile כולן מחזירות Promises שמדמות תהליך מדורג.
  • כל שלב תלוי בשלב הקודם ומתבצע רק אחרי שהשלב הקודם הצליח.
  • הפלט יהיה:
Checking file...
File exists
Downloading file...
File downloaded
Processing file...
File processed
Process completed.

סיכום

בפרק זה למדנו על:

  1. מהו Promise וכיצד הוא פותר בעיות בקוד אסינכרוני.
  2. איך ליצור Promise וכיצד להשתמש ב-then ו-catch כדי להגיב להצלחות וכישלונות.
  3. עבודה עם Promise.all ו-Promise.race כדי לטפל ב-Promises מרובים במקביל.
  4. שרשור פעולות עם Promises (Chaining) להמשך פעולות על בסיס תוצאות קודמות.
  5. שימוש ב-finally כדי לבצע פעולות לאחר סיום ה-Promise.

בפרק הבא נעמיק בנושא Async/Await, שיפור נוסף לניהול קוד אסינכרוני שמבוסס על Promises ומאפשר לכתוב קוד קריא יותר.

Scroll to Top
דילוג לתוכן