אינטרנט ישראל
  • ראשי
  • אודות רן בר-זיק ואינטרנט ישראל
  • ערוץ טלגרם
  • מסטודון
  • התחברו אלי בטוויטר
  • התחברו אלי בלינקדאין
  • ספר ג'אווהסקריפט
  • ראשי
  • אודות רן בר-זיק ואינטרנט ישראל
  • ערוץ טלגרם
  • מסטודון
  • התחברו אלי בטוויטר
  • התחברו אלי בלינקדאין
  • ספר ג'אווהסקריפט
ראשי » מדריכים » ES20XX » ES2017 async

ES2017 async

רן בר-זיק מרץ 5, 2017 7:07 am 8 תגובות

שימוש ב-async הופך קריאות אסינכרוניות, שמאוד מאפיינות את עולם הג׳אווהסקריפט, למאוד אינטואיטיביות

»ES2016 והגרסאות שאחריו
»ES2017 – פסיקים בסוף
כדאי תמיד להשאר מעודכנים! אם יש לכם טלגרם, בדקו את ערוץ הטלגרם של האתר שבו אני מעדכן על פוסטים חדשים 🙂 אם אתם רוצים ללמוד תכנות באופן מקיף ומסודר, הצטרפו לאלפי הלומדים בפרויקט "ללמוד ג'אווהסקריפט בעברית" שמלמד לתכנת בג'אווהסקריפט, ב-Node.js ובריאקט וגם מלמד על תרומה לקוד פתוח. גם ספרים דיגיטליים וגם ספרים מודפסים. בשיתוף הקריה האקדמית אונו ובתמיכת חברות מובילות כגון Wix, Outbrain, Elementor, Iron Source, Chegg, Really Good ועוד.

אני יודע שasync נשמע כמו להקת בנים עלובה משנות ה-90, אבל האמת היא שמדובר באחד מהפיצ׳רים שהכי מחכים להם בתקן ES2017 החדש. כזכור – אפשר להשתמש בו כבר היום עם babel. הוא ממש ממש חדש ודנדש ואין אותו אפילו ב-node 8. שלא לדבר על דפדפנים (נכון לכתיבת השורות האלו).
מדובר בפיצ׳ר קצת קשה להבנה, אז אשמח אם לא תדלגו לדוגמאות אלא תקראו את המאמר הזה מההתחלה ועד הסוף. אני מבטיח שזה ישתלם לכם כי זה פיצ׳ר ממש ממש גאוני. מוכנים? שופופו:

כולנו מכירים promises, נכון? מי שלא מכיר – זה הזמן להזכר בes2015. מדובר בדרך מאוד מאוד נוחה לעבוד עם קוד שלוקח לו זמן להתבצע אחר כך. למשל קריאה מ-API או קריאה ממסד נתונים או מערכת קבצים. כאן למשל אני אדגים באמצעות קוד שפשוט ממתין כמה שניות עד שהוא חוזר:


function someService() {
  return new Promise(function(resolve, reject) {
    setTimeout(ev => { 
      resolve('Promise result123'); 
    }, 500);
  });
}

console.log('start of code');
someService().then(result => {
  console.log(result);
});
console.log('end of code');

//Will print:
//"start of code"
//"end of code"
//"Promise result123"


מה יש לנו פה? משהו שמתכנת js מנוסה אמור להכיר. someService היא פונקציה שבאופן עקרוני מחזירה promise אחרי חצי שניה. כרגע מדובר במשהו מזויף, אבל זו יכולה להיות קריאת API או קריאה לקובץ. מה שחשוב הוא שאני משתמש בה באמצעות then. שימוש סטנדרטי של promise ואני אראה שבאמת ברגע שה-promise מתממשת, מה שהיא מחזירה יודפס.
אם הקוד הזה נראה לכם לא מובן בגלל כל ה-=> וה-let, זה הזמן להתחיל לעבוד עם ES6 כי זה הסינטקס שעובדים איתו.

עם async אני יכול לעשות אותו הדבר, רק בהרבה פחות קוד. שימו לב לקוד הזה:


function someService() {
  return new Promise(function(resolve, reject) {
    setTimeout(ev => { 
      resolve('Promise result123'); 
    }, 500);
  });

}
async function main() {  
  let result = await someService();
  console.log('result' + result);
}

console.log('start of code');
main();
console.log('end of code');

//Will print:
//"start of code"
//"end of code"
//"Promise result123"

מה שמעניין וחשוב כאן הוא ה-main. במקום להשתמש ב-then, אנחנו משתמשים ב-await. בלי resolve ובלי בלגנים! מה שעושים הוא להגדיר פונקציה מסוימת כאסינכרונית. את זה אנחנו עושים באמצעות הכנסת המילה השמורה async לפני ה-function. מהרגע הזה אנחנו יכולים להשתמש ב-await בלי תסבוכות ובלגנים. ה-await בעצם קורה ברגע שההבטחה מתממשת. כלומר ברגע שיש לנו resolve בלי צורך לכתוב then!

הנה דוגמה ממש חיה – שימו לב שכאן הפעלתי את ה-babel עם ה-async plugin וגם את ה -polyfill:

See the Pen Async example by Ran Bar-Zik (@barzik) on CodePen.

אם אנחנו רוצים דוגמה מעט יותר מוחשית, אפשר להסתכל על הקוד הזה:


function jokeService() {
  return new Promise(function(resolve, reject) {
    var request = new XMLHttpRequest();

    request.open('GET', 'https://api.icndb.com/jokes/random');
    request.onload = function() {
      if (request.status == 200) {
        resolve(JSON.parse(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
  });

}

async function main() {  
  let result = await jokeService();
  document.write(result.value.joke);
}

main();



אפשר להתחיל לנתח את jokeService אבל היא לא מאוד מעניינת. בגדול פונקציה אסינכרונית שקוראת ל-API כלשהו – מה שחשוב היא שהיא מחזירה לנו promise. אני יכול לקרוא לה ובאמצעות then לחלץ ממנה את התוצאה. אבל למה? אני יכול לעשות את זה באופן יותר אינטואטיבי ויותר קל. אני אצור פונקציה ואשתמש בתוצאה של jokeService ברגע שהיא תהיה מוכנה בקוד רגיל באמצעות שימוש ב-async\wait! כמה קל, נעים ופשוט! await פשוט מחליף את then. זה הכל! זה הביג דיל!

הנה הדוגמה החיה שאפשר לשחק איתה:

See the Pen Async example – joke service by Ran Bar-Zik (@barzik) on CodePen.

async היא קלה לשימוש גם כאשר יש לי הבטחות מרובות:


function jokeService() {
  return new Promise(function(resolve, reject) {
    var request = new XMLHttpRequest();

    request.open('GET', 'https://api.icndb.com/jokes/random');
    request.onload = function() {
      if (request.status == 200) {
        resolve(JSON.parse(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
  });

}

function fortuneService() {
  return new Promise(function(resolve, reject) {
    var request = new XMLHttpRequest();

    request.open('GET', 'https://helloacm.com/api/fortune/');
    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
  });

}

async function main() {  
  let joke = await jokeService();
  let fortune = await fortuneService();
  document.write(joke.value.joke + fortune);
}

main();



כאן יש לי שני שירותים – jokeService ו-fortuneService. שניהם אסינכרוניים כי הם הולכים לרשת ומביאים לי תוצאה מ-API. במקום לשבור את הראש על all ו-then, אני משתמש ב-async וב-await. תראו כמה זה קל ונעים והכי חשוב- אינטאוטיבי. רק כששני השירותים מוכנים, הם מחזירים את התוצאות והקוד בתוך הפונקציה האסינכרונית יכול לרוץ.

הנה הדוגמה החיה:

See the Pen Async example – multiple services by Ran Bar-Zik (@barzik) on CodePen.

אלו שיותר מנוסים ב-promises יראו כמעט מייד את הבעיה פה. הבעיה היא שבניגוד ל-then, אין לנו יכולת להחליט מה יקרה אם ה-promise ייכשל. הרי במימוש רגיל של promise, ה-then מקבל שתי פונקציות – אחת שמחליטה מה קורה אם ההבטחה מתממשת והשניה מה קורה אם לא. אני אדגים למי ששכח:


function someService() {
  return new Promise(function(resolve, reject) {
    setTimeout(ev => { 
      reject('Promise rejected'); 
    }, 500);
  });
}

someService().then(
  result => {}, //Will never happened
  err => {console.log(err)} //Promise rejected
)

הנה הדוגמה החיה:

See the Pen Rejected promise by Ran Bar-Zik (@barzik) on CodePen.

מה יש לנו פה? יש לי את someService שתמיד עושה reject. כשאני קורא לו, אני מעביר ב-then שתי פונקציות. הראשונה שתפעל אם הכל תקין והשניה שתפעל במקרה של reject. כיוון ש-someService היא ילד קקה, היא תמיד תעשה reject ותמיד נקבל הדפסה של שגיאה. איך אני מממש דבר כזה עם async ו-await? מאוד פשוט. עם try catch! כיוון ש-async חוזרת בעצם לסינטקס הרגיל של ג׳אווהסקריםט, אין שום בעיה להשתמש בסינטקס הרגיל של השפה!


function someService() {
  return new Promise(function(resolve, reject) {
    setTimeout(ev => { 
      reject('Promise rejected :P'); 
    }, 500);
  });
}

async function main() {
  let result;
  try {
    result = await someService(); 
    console.log(result); //Will never happen
  } catch(err) {
    document.write(err); //"Promise rejected :P"
  }
}

main();


ממש ממש קל, לא? הנה הדוגמה החיה למי שרוצה לשחק:

See the Pen Rejected async by Ran Bar-Zik (@barzik) on CodePen.

כפי שאםפשר לראות, למרות ש-async נראה מאיים ומפחיד קצת, הוא בעצם מפשט מאוד את החיים ומקל עליהם. אין שום סיבה שלא להשתמש בו כבר היום ולהמיר את המימוש של הpromises לקוד יותר אלגנטי. לא לשכוח שה-promises לא נעלמות לשום מקום ואנחנו משתמשים בהן ליצירת הפונקציות האסינכרוניות שאנו קוראים להם.
כמו כן, נכון לעכשיו, אין דרך מספיק טובה לעשות אמולציה ל promise.all.

ה-async, בגדול, הוא סוכר סינטקסטי מעל הגנרטורים. כלומר המנגנון שמפעיל אותו הוא זה של הגנרטורים והוא עובד בדיוק כמוהם. ה-await הוא בעצם ה-yield. אם תסתכלו על הקוד ש-babel עובר עליו תראו שהוא עושה את הטרנספורמציה הזו מ-async לגנרטורים. אבל זה באמת לא נדרש לשימוש ראשוני והבנה בסיסית של async.

כדאי תמיד להשאר מעודכנים! אם יש לכם טלגרם, בדקו את ערוץ הטלגרם של האתר שבו אני מעדכן על פוסטים חדשים 🙂 אם אתם רוצים ללמוד תכנות באופן מקיף ומסודר, הצטרפו לאלפי הלומדים בפרויקט "ללמוד ג'אווהסקריפט בעברית" שמלמד לתכנת בג'אווהסקריפט, ב-Node.js ובריאקט וגם מלמד על תרומה לקוד פתוח. גם ספרים דיגיטליים וגם ספרים מודפסים. בשיתוף הקריה האקדמית אונו ובתמיכת חברות מובילות כגון Wix, Outbrain, Elementor, Iron Source, Chegg, Really Good ועוד.
es2017 JavaScript

8 תגובות

  1. אבי הגב מרץ 5, 2017 בשעה 10:09 am

    יפה מאד!
    פיצ'ר שקיים כבר מזמן בשפות נורמליות אחרות (כמו C# למשל..),
    ובאמת הגיע הזמן שיכנס גם לJS..

    תודה על ההשקעה והכתיבה בעברית!!

  2. Eyal הגב מרץ 5, 2017 בשעה 12:49 pm

    Node 7.6.0 תומך בasyncגם ללא harmony flag

    • רן בר-זיק הגב מרץ 31, 2017 בשעה 6:09 pm

      צודק! תודה 🙂

  3. ישי הגב מרץ 5, 2017 בשעה 1:22 pm

    כרגיל, הסבר ברור ומובן גם לדברים שקודם היו נראים מסובכים.
    תודה רבה

  4. קורא קבוע הגב מרץ 28, 2017 בשעה 1:44 am

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

    • רן בר-זיק הגב מרץ 31, 2017 בשעה 6:09 pm

      תודה! אין לי אלא להסכים עם מה שכתבת.

      • משתמש אנונימי (לא מזוהה) הגב יולי 18, 2019 בשעה 10:35 am

        מה שאתה צריך לעשות זה לדוגמא במקרה שלך במקום:
        let joke = await jokeService();
        let fortune = await fortuneService();
        לכתוב:
        let [joke, fortune] = await Promise.all(jokeService(), fortuneService());

  5. משתמש אנונימי (לא מזוהה) הגב פברואר 18, 2020 בשעה 3:55 pm

    תודה!
    ההסבר ברור ופשוט.

השארת תגובה

ביטול

ללמוד ג'אווהסקריפט בעברית

ללמוד לתכנת ג'אווהסקריפט בעברית שגייס יותר משלוש מאות אלף שקל ולמעלה מ-2000 תומכים - בואו ללמוד עכשיו איך לתכנת.

רשימת הנושאים
  • מדריכים
    • ריאקט
    • טייפסקריפט
    • ECMAScript 6
    • ES20XX
    • Node.js
    • Express
    • רספברי פיי
    • Babel
    • docker
    • MongoDB
    • Git
    • לימוד MySQL
    • SASS
    • jQuery
    • CSS3
    • HTML 5
    • SVN
    • LESS
  • פיתוח אינטרנט
    • פתרונות ומאמרים על פיתוח אינטרנט
    • jQuery Scripts
    • jQuery למתקדמים
    • יסודות בתכנות
    • נגישות אינטרנט
  • חדשות אינטרנט
  • מידע כללי על אינטרנט
    • רשת האינטרנט
    • בניית אתרי אינטרנט
  • rss logo

    לכל המאמרים

    לכל המאמרים שפורסמו באינטרנט ישראל משנת 2008 ועד עכשיו.
  • rss logo

    RSS Feed

    משתמשים בקורא RSS? אם כן, עקבו אחרי אינטרנט ישראל באמצעות פיד ה-RSS!
    מה זה RSS?
  • Twitter logo

    עקבו אחרי בטוויטר

    בחשבון הטוויטר שלי אני מפרסם עדכונים מהירים על חדשות בתחום התכנות והיזמות, התרעות על מצבי חירום ורכילות בוערת על תחום הווב.
    מה זה טוויטר?
  • facebook like image

    ערוץ הטלגרם של אינטרנט ישראל

    בערוץ הטלגרם של אינטרנט ישראל אני מפרסם את הפוסטים של באתר וכן עדכונים טכנולוגיים נוספים.
    מה זה טלגרם?
  • github logo

    הפרויקטים שלי בגיטהאב

    הפרויקטים שאני כותב ושוחררו לציבור ברישיון קוד פתוח נמצאים ברובם בגיטהאב.
חיפוש

כל הזכויות שמורות לרן בר-זיק ולאינטרנט ישראל | מדיניות הפרטיות של אתר אינטרנט ישראל | אתר אינטרנט ישראל נגיש לפי תקן WCAG 2.0 AA | הצהרת הנגישות של האתר | אבטחת מידע ודיווח על בעיית אבטחת מידע

גלילה לראש העמוד