iT邦幫忙

2024 iThome 鐵人賽

DAY 17
1
Modern Web

JavaScript學習筆記系列 第 17

[Day 17] 非同步 Async / await 語法糖

  • 分享至 

  • xImage
  •  

async/await 背後的原理其實就是Promise,但async/await 使用起來會更簡潔、更好閱讀。所以在使用之前,建議要先學好Promise的觀念~

Async

語法

async function name(param0) {
  statements
}
async function name(param0, param1) {
  statements
}
async function name(param0, param1, /* …, */ paramN) {
  statements
}

MDN說明:

Async functions always return a promise. If the return value of an async function is not explicitly a promise, it will be implicitly wrapped in a promise.

加上Async關鍵字的函式,會回傳一個Promise,即便這個值不是Promise,JavaScript也會包裝成一個Promise回傳。

範例:

async function getData(){
  return 2; //設定回傳數值2
}
getData();

執行結果:
async
原本getData函式回傳為數值2,但加上了async關鍵字,就一定回傳一個Promise,所以數值2被包裝成一個Promise物件回傳 Promise {<fulfilled>: 2}

其實可以就看成這樣:

async function getData(){
  return Promise.resolve(2); //直接發出成功的Promise
}
getData();

Await

Async functions can contain zero or more await expressions. Await expressions make promise-returning functions behave as though they're synchronous by suspending execution until the returned promise is fulfilled or rejected.

意思是加上async關鍵字的函式,裡面可以有0到多個await表達式。
await會等待後方的表達式(Promise)有結果,才會繼續往下執行程式,所以看起來會像是同步的

範例:

const myPromise = new Promise(function (resolve,reject) {
  setTimeout(function () {
    resolve("成功!");
  }, 1000);
});

async function getData() {
  let result = await myPromise;
  return result; 
}
getData(); //"成功!"

myPromise是一個Promise物件,JavaScript會等待await後方的myPromise有結果了,再把結果給result變數,所以呼叫getData會拿到resolve的結果。

try...catch只能抓到同步的錯誤

MDN:
Use of async and await enables the use of ordinary try / catch blocks around asynchronous code.

我們要先知道,try...catch只能抓到同步的錯誤!
用範例來說明,當try...catch裡面有非同步程式,會無法捕捉到錯誤:

//先建立非同步函式
function myFunction() {
  setTimeout(function () {
    throw new Error("非同步錯誤");
  }, 2000);
}

try {
  myFunction();
  console.log("try區塊");
} catch (error) {
  console.log(error.message);
}
console.log("try/catch區塊之外");

執行結果:
try...catch無法捕捉非同步錯誤

  1. 主執行緒開始執行try區塊,呼叫myFunction函式,setTimeout計時2秒後,瀏覽器會把callback function推到事件佇列註冊起來,並等待執行。但主執行緒不會等待這2秒。
  2. try區塊的console.log("try區塊")立即執行,並印出"try區塊"。
  3. try...catch區塊外的console.log("try/catch區塊之外")立即執行並印出。因為主執行緒不會等待setTimeout完成,會直接執行 try...catch 區塊之後的程式碼。
  4. 等到JavaScript主執行緒的try..catch都完成,再來執行事件佇列中的事件,也就會在try...catch區塊之外執行callback function。
  5. 此時new Error的錯誤沒有try...catch捕捉,所以錯誤會拋到全域,瀏覽器會顯示錯誤訊息。

async/await搭配try...catch

剛剛說到try...catch無法捕捉非同步的錯誤,怎麼又說搭配一起??
這是因為await關鍵字會使Promise變為同步的執行結果,所以try...catch就可以正常執行。

來改寫之前的範例:

const myPromise = new Promise(function (resolve, reject) {
  setTimeout(function () {
    reject("錯誤!");
  }, 1000);
});

async function getData() {
  try {
    let result = await myPromise;
    console.log(result);
    return result;
  } catch (error) {
    console.log(error);
    return "有錯誤!";
  }
}
getData(); //"有錯誤!"

以上看到async/await中,try...catch可以直接捕捉到Promise的錯誤,就不需要使用then()或catch()。

Promise無法使用try...catch

Promise則無法使用try...catch捕捉錯誤,必須搭配then()和catch()。

const myPromise = new Promise(function (resolve, reject) {
  setTimeout(function () {
    reject("錯誤!");
  }, 1000);
});

try {
    console.log("try");
    myPromise
        .then(function(result) {
            console.log(result);
        })
        .catch(function(error){
            console.log("Promise 錯誤捕捉:", error); // 錯誤在這裡抓到
        });
} catch (error) {
    console.log("無法捕捉到這個錯誤"); // 這裡永遠不會被執行
}
console.log(123);

以上分享~謝謝

參考資料

MDN - async function
MDN - await
JS 原力覺醒 Day16 - Async / Await:Promise 語法糖
[Javascript] ES7 Async Await 聖經


上一篇
[Day 16] 非同步 Promise
下一篇
[Day 18] this - 上
系列文
JavaScript學習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言