1. מה זה Promise?
Promise הוא אובייקט שמייצג פעולה אסינכרונית (פעולה שמתבצעת ברקע) והערך שלה עשוי להיות מוכן בעתיד. מדובר במנגנון שמקל על עבודה עם קוד אסינכרוני ומאפשר לכתוב קוד קריא וקל לניהול, במיוחד לעומת השיטה המסורתית של Callbacks.
במילים פשוטות, Promise היא "הבטחה" לערך שיגיע בעתיד. כאשר הפעולה האסינכרונית מסתיימת, ה-Promise יכול להימצא באחת משלוש מצבים:
- Pending (בהמתנה): הפעולה האסינכרונית עדיין מתבצעת.
- Fulfilled (התקיימה): הפעולה הסתיימה בהצלחה וה-Promise מחזיר תוצאה.
- Rejected (נכשלה): הפעולה נכשלה וה-Promise מחזיר שגיאה.
2. יצירת Promise
יצירת Promise נעשית באמצעות המחלקה Promise
. פונקציית ה-Promise מקבלת פונקציה אחרת עם שני פרמטרים:
- resolve: פונקציה שמופעלת כשהפעולה האסינכרונית הצליחה.
- 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.
סיכום
בפרק זה למדנו על:
- מהו Promise וכיצד הוא פותר בעיות בקוד אסינכרוני.
- איך ליצור Promise וכיצד להשתמש ב-
then
ו-catch
כדי להגיב להצלחות וכישלונות. - עבודה עם
Promise.all
ו-Promise.race
כדי לטפל ב-Promises מרובים במקביל. - שרשור פעולות עם Promises (Chaining) להמשך פעולות על בסיס תוצאות קודמות.
- שימוש ב-
finally
כדי לבצע פעולות לאחר סיום ה-Promise.
בפרק הבא נעמיק בנושא Async/Await, שיפור נוסף לניהול קוד אסינכרוני שמבוסס על Promises ומאפשר לכתוב קוד קריא יותר.