JS Promises הוא פיצ'ר של EcmaScript 6. ניתן להשתמש בו כמובן ב-Node.JS ואפילו בצד הלקוח בכל הדפדפנים חוץ מאינטרנט אקספלורר כמובן (כל הגרסאות).
אז מה זה? בגדול לא משהו שיפיל אתכם מבחינת הקונספציה, דרך נוחה לתפוס אירועים אסינכרוניים. ולהגיב אליהם.
בואו ונחשוב למשל על קוד AJAX פשוט שמבצע איזושהי קריאה לשרת ומחזיר תוצאות. בדרך כלל, כשיש לי קוד כזה, מדובר בחגיגת callbacks מהסוג שאנחנו לא אוהבים כל כך לעשות. במיוחד אם מדובר בקוד מורכב יחסית. הנה דוגמה לחגיגה לא נחמדה שכזו:
object.save({ key: value }, { success: function(object) { // the object was saved. lets do something! doSomething(function(result) { //doing something and then, when complete, we will do another thing doAnotherThing(function(result) { //and the party goes on! }); }); }, error: function(object, error) { // saving the object failed. } });
יש דרכים להמנע מהדבר הזה, אבל עם js promises הרבה יותר קל לעשות את זה. בואו ונראה איך!
בגדול promises זה אובייקט חביב שמקבל פונקציה עם שני פרמטרים: resolve ו reject. בתוך ה-promise אנחנו יכולים לעשות הכל, אבל הכל, אבל ממש הכל. כשאנחנו מסיימים אנחנו מחזירים resolve (אפשר עם פרמטרים) במקרה והכל מצוין, או reject במידה והכל נכשל. ככה זה נראה:
var myPromise = new Promise(function(resolve, reject) { resolve(paramater); // Promise resolved! :) reject(parameter2); // Promise rejected! :( }; }); myPromise.then(function(paramater) { console.log('Got data! Promise fulfilled.'); }, function(parameter2) { console.log('Promise rejected.'); })
יש כאן שני חלקים – ה-promise שעוטף בעצם את הפעולה עצמה – במקרה הזה אין ממש פעולה, מה שחשוב זה להחזיר את resolve ואת reject.
החלק השני הוא מה אני עושה כאשר ה-promise נכשל או מצליח. וזה החלק השני – אני משתמש ב-then שמקבלת שני פרמטרים – פונקציה אנונימית ראשונה שהיא רצה כאשר יש לנו resolve ופונקציה שניה שרצה כאשר יש לנו reject.
אולי זה יהיה יותר ברור לא עם דוגמה אבסטרקטית אלא עם דוגמה ממש ממשית שלקחתי אותה מהמאמר המצוין הזה ב-sitepoint. כי התעצלתי לכתוב משהו משלי:
var promise = new Promise(function(resolve, reject) { var request = new XMLHttpRequest(); request.open('GET', 'http://api.icndb.com/jokes/random'); request.onload = function() { if (request.status == 200) { resolve(request.response); // we got data here, so resolve the Promise } else { reject(Error(request.statusText)); // status is not 200 OK, so reject } }; request.onerror = function() { reject(Error('Error fetching data.')); // error occurred, reject the Promise }; request.send(); //send the request }); promise.then(function(data) { console.log('Got data! Promise fulfilled.'); document.getElementsByTagName('body')[0].textContent = JSON.parse(data).value.joke; }, function(error) { console.log('Promise rejected.'); console.log(error.message); })
הפעם בתוך ה-promise יש לנו אשכרה קוד שעושה משהו. במקרה הזה שולח בקשה ל-API של בדיחות (נשבע לכם). במידה והבקשה מצליחה, אנחנו מקבלים 200 ואז מפעילים את ה-resolve ומעבירים כפרמטר את התגובה. במידה והבקשה נכשלת, אנו מעבירים reject עם השגיאה.
בחלק השני, אנו קובעים מה עושים כאשר יש הצלחה (פונקציה אנונימית ראשונה ב-then) ומה קורה אם זה נכשל (פונקציה אנונימית שניה).
טוב, עד כאן ממש מגניב, אבל מה יעיף לכם את הסכך באמת? רואים את ה-then הזה? אפשר להעביר בו שני פרמטרים של הצלחה וכשלון, אבל לא דיברנו על מה שהוא מחזיר. מה הוא מחזיר אתם שואלים? האם הוא מחזיר אהבת אמת? לא. משהו יותר טוב! מה יכול להיות יותר טוב מאהבת אמת? התשובה היא: עוד promise! ואליו אפשר לשרשר עוד then. איך? משהו בסגנון הזה:
var myPromise = new Promise(function(resolve, reject) { resolve(paramater); // Promise resolved! :) reject(parameter2); // Promise rejected! :( }; }); myPromise.then(function(paramater) { console.log('Got data! Promise fulfilled.'); return data; }, function(parameter2) { console.log('Promise rejected.') .then(function(data){ //do another thing, it will be called after the first promise was fullfilled. }). .then(function(data){ //do third thing, it will be called after the last promise was fullfilled. });
אם נקשור את זה לדוגמה הקודמת שלנו, אז אנחנו יכולים לעשות משהו כזה.
if(window.Promise){ console.log('Promise found'); var promise=new Promise(function(resolve,reject){ var request = new XMLHttpRequest(); request.open('GET', 'http://api.icndb.com/jokes/random'); request.onload = function() { if (request.status == 200) { resolve(request.response); //we get the data here.So, resolve the Promise } else { reject(Error(request.statusText)); //if status is not 200 OK, reject. } }; request.onerror = function() { reject(Error("Error fetching data.")); //error occurred, reject the Promise }; request.send(); //send the request }); console.log('Asynchronous request made.'); promise.then(function(data){ console.log('Got data! Promise fulfilled.'); document.getElementsByTagName('body')[0].textContent=JSON.parse(data).value.joke; return 'Laugh level - very funny!'; },function(error){ console.log('Promise rejected.'); console.log(error.message); }) .then(function(data){ console.log('success : '+data); }); } else console.log('Promise not available');
הנה קישור לדוגמה שעובדת – אתם מוזמנים לשחק שם וליצור גרסה משלכם!
יש עוד פיצ'רים מעניינים ל-promise, אבל זה הבסיס. במאמר המשך אסקור את כל שאר הדברים. מבטיח.
כמובן שהשרשור הזה יכול לפתור לנו את חגיגת הקולבקים המוכרת הן מ-Node.JS והן ממקומות אחרים. האפשרות שלי לקחת כמה then ולהכניס בכל אחד מהם פעולות שתלויות בקודמו ולסמוך על promises היא גדולה מהחיים. נכון לעכשיו רק ב-Node.JS ובקליינט רק בדפדפנים שהם לא אקספלורר. דפדפן אדג' אמור לתמוך בזה.
ככל שאקספלורר הולך ונעלם, יש מצב שזה יהיה רלוונטי לא רק לקוד של בק אנד אלא גם לקוד של פרונט אנד. אפשר לראות עד כמה זה קל ונעים לכתוב קוד תוך שימוש ב-promises. הייתי מציע לכם להשקיע כמה דקות בללמוד את זה, זה שימושי מאוד ויהיה שימושי בקרוב עוד יותר.
2 תגובות
במאמר שלך (אם הבנתי נכון) יש לpromise מטודה בשם then שמקבלת 2 ארגומנטים, הראשון מופעל במקרה של resolve והשני במקרה של reject. אבל בכל המקומות האחרים ראיתי שזה עובד בכלל אחרת, יש שתי מטודות שונות(!) לאובייקט promise: אחת בשם then, עם פרמטר יחיד, שתופעל במקרה של resolve ואחת בשם catch, עם פרמטר יחיד, שתופעל במקרה של reject. איך זה מסתדר עם המאמר שלך? אולי יש שתי אפשרויות להתנהל עם האובייקט הזה?
ובלי קשר אשמח לעזרה, יש משהו שלא ממש הבנתי. מתי הקוד שכתוב בתוך promise מתחיל לרוץ? כלומר אם נסתכל על סדר ההרצה של התוכנית, מתי נראה שהקוד בpromise מורץ? ברגע שהפעלנו את then? ואם כן אז מה מיוחד בכל האובייקט הזה בעצם? איפה האסינכרויות פה? אנחנו בסך הכל מפעילים קוד וממתינים כמו גדולים שהוא יסתיים.. איפה פה הלעשות שני דברים במקביל?
מקווה שהבנת את השאלה שלי,
בכל מקרה תודה רבה על המאמרים שלך, העולם של הweb ממש חדש לי והאתר שלך מציל אותי:)
כדאי לקרוא גם את הדוקס של מוזילה, יש שם הסבר:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#Chaining_after_a_catch