iT邦幫忙

2022 iThome 鐵人賽

DAY 17
0
Modern Web

JavaScript 之路,往前邁進吧!系列 第 17

JS之路 Day17 - async/await(Promise語法糖)

  • 分享至 

  • xImage
  •  

前言

async/await的方式提供了promise一種新的寫法,至於有沒有更方便及更高的可讀性,我覺得使用了async/await是更容易進行編寫的,至少可以不用一直.then下去,使用了async/await之後,就跟.then說再見。

今天會介紹的順序:

  1. 這兩個好用的關鍵字分別代表的意思。
  2. async/await 所達成的「同步」效果。
  3. 錯誤處理的應對措施。

async

這是一個加在function前面的關鍵字,沒有加就是普通的function,加了之後就會變成async function就會代表著現在這個function是一個非同步的function

// 普通的function
function add(num) {
  return 10 + num;
}  

// async function 
async function add(num) {
  return 10 + num;
}  

而會說加上asyncfunction會變成非同步的原因主要是因為async會讓function在回傳的時候會返回一個promise,所以因為是promise,所以也可以使用.then來取出結果。

async function add(num) {
  return 10 + num;
}

add(10).then(console.log); // 20

所以跟這樣也是會相同的,不加上asyncfunction

function add(num) {
  return Promise.resolve(10 + num);
}
add(10).then(console.log); //20

加了asyncasync function除了可以讓整體變成非同步的function之外,還可以使用一種名為await的招式。

await

這些從它的單字意思其實就能略知一二,await是等待,在這裡當關鍵字的就是就是讓這個async function進行等待。

這邊很重要的一點就是,一定要是async function,沒有的話就沒辦法使用,可是說是有async才有await(但沒有await也可以用async)。

function add() {
  let result = await promise;
} // SyntaxError

沒有async的狀態下直接使用await就會報錯給你看。

等待主要是讓Promise的運行先暫停一下,直到接受了回傳值才會繼續運行,而await這種藉由等待,暫停了非同步的執行的方式,是讓使用了async/awaitfunction看起來很像同步的關鍵。

明明是非同步,為什麼會看起來很像同步呢? 看過來。

async/await的「同步」

 const promise = new Promise(function (resolve) {
  setTimeout(() => resolve("五秒到了"), 5000);
});

async function sec() {
  console.log("在 await 之前");

  let result = await promise;
  console.log(result);

  console.log("在 await 之後");
}

sec(); //?

在講這段程式之前,先來談談順序的問題。

一般來說,程式碼看下來的流程,會先去執行同步的程式,因為不會有需要等待排隊的問題,而非同步的因為沒辦法馬上完成,所以要等待後才能執行,等待的時間不知道,但能知道的是一定同步的東西先跑完。

程式碼的寫法順序

同步1
非同步1
同步2
同步3

實際上的執行順序:

同步1
同步2
同步3

...... => 確保同步都跑完

非同步1

基本上有兩個概念在執行順序這一塊:

  1. 會由上而下執行
  2. 同步先執行,才是非同步執行

回到程式碼,照著剛剛的概念來看,應該會是先跑出來在 await 之前之後是在 await 之後,而經由了setTimeout而造成需要等待幾秒才會執行的promise非同步理論上應該是最後才會跑出來,但是如果你把上面那段程式碼拿去執行,你會發現順序是:

在 await 之前
五秒到了
在 await 之後

怎麼跟上面講的不一樣呢?這其實就是async/await的神奇之處,使用了await就會等待,就算它本身是一個非同步的運行,在整體的async function之中,也會逐行的執行,不會有某一行被抓去排隊(事件佇列)而執行跳到下一行的狀況,看起來就可以達成是非同步的,但我用同步的方式去跑。

錯誤處理

await後面接的promise會直接回傳結果,而結果有兩種可能性。

  • 成功 => resolve
  • 失敗 => reject

在原本使用promise的時候,會使用.then.catch去獲取這兩種結果,但是async/await不用。

我研究之後發現在async/await時會使用try..catch的方式來處理錯誤的結果。

try..catch

它會分成兩個區塊,一個try 一個catch,主要就是正常是try區塊,在try區塊碰到錯誤,直接跳到catch區塊。

try {
    // 要執行的程式
} catch(error) {
    // ...
    // 錯誤發生時,上面的就不會執行,改執行這邊
}

舉例時間:

try {
  console.apple("apple不是正確的使用方式喔");
} catch (e) {
  console.log("我錯了");
}

這段code的結果會印出我錯了,如下:

為什麼會發生這樣的結果?這是因為如果try區塊裡面的程式碼假如沒有任何的錯誤,就會直接忽略掉catch區塊裡面的程式碼,反之有錯的話,就會以catch裡面的程式碼為主,而錯誤的try就會中斷執行,所以在這個範例來說,因為try寫錯了,所以會以try裡面的程式碼為主。

另外如果catch區塊如果接受了一個參數,就可以直接利用這個參數獲取錯誤資料,一般都會是直接在後台輸出資料,這邊來試試看:

try {
  console.apple("apple不是正確的使用方式喔");
} catch (e) {
  console.log("錯誤訊息 :", e);
}

這段code的結果會印出錯誤資訊,如下:

async/await遇錯時

使用剛剛講的方式,先把全部的async function都用try..catch包起來,然後錯誤的話自然就會跑到catch區塊自動去抓取失敗的結果。

function promise(a) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (a) {
        resolve("五秒到了");
      } else {
        reject("錯誤發生");
      }
    }, 5000);
  });
}

async function sec() {
  try {
    console.log("在 await 之前");

    let result = await promise(0);
    console.log(result);

    console.log("在 await 之後");
  } catch (error) {
    console.log(error);
  }
}

sec();

最後結果印出:

在 await 之前
錯誤發生

第一行的在 await 之前還是會出現,只是經過了下方awaitpromise,因為裡面帶入的參數是負數,所以是false,結果會跑去else reject("錯誤發生"),而最終會進到catch區塊直到結束,所以try區塊後面的在 await 之後就不會看到。

要注意一件事情,try區塊可以因為錯誤而進到catch區塊,但沒辦法反向,也就是說無法在失敗之後再度執行原本程式。

總結

async function的奇妙之處:

  1. 內部可以使用名為await的招式。
  2. 內部最終總是會回傳一個promise
  3. 整體會變成非同步的狀況。

await這個招式使用之後的兩種下場:

  1. 強制promise等待直到完成。
  2. 有錯誤的話,拋出reject的錯誤結果。
  3. 沒錯誤就直接回傳resolve的成功結果。

什麼時候可以用?

基本上當你想要使用promise.then時,都可以把它改成async/await的方式,就不用一直.then,處理非同步的程式時又多了一種寫法,看完這這篇後,大家也多嘗試用看看async/await來寫code吧。

reference

[1] MDN - async function
[2] MDN - await
[3] w3schools - JavaScript Async


上一篇
JS之路 Day16 - Promise methods(承諾方法)
下一篇
JS之路 Day18- What is Biglnt?
系列文
JavaScript 之路,往前邁進吧!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言