iT邦幫忙

2022 iThome 鐵人賽

DAY 20
2

前言

在前幾天認識了 Promise 後,還有其他作非同步處理的方式,所以這篇來介紹一下 async/await。


async/await 基本介紹

async/await 為 Generator + Promise 的語法糖,這個語法可以讓開發者撰寫看起來像同步的非同步程式碼async 會加在函式前面,該函式被呼叫後會返回一個 Promise 物件

並且如果該函式回傳了一個值,Promise 的狀態將為一個帶有該回傳值的 resolved/fulfilled。如果該函式拋出例外或某個值,Promise 的狀態將為一個帶有被拋出值的 rejected。

可以看以下截圖運行結果,exampleFunc 回傳了一個值 hi,所以是 resolved/fulfilled 狀態:

而 await 要在加上 async 的函式內執行,它會暫停此 async 函式的執行,從程式執行面看的話:

  1. 它會暫停此 async 函式的執行,await 後的函式會先執行一次
  2. 接著跳出整個 async 函式,執行後面的程式碼
  3. 等後面的同步任務執行完後又會跳回 async 函式中執行 await 那行程式碼之後後面的程式碼

這樣說可能蠻抽象,所以讀者可以將這三個步驟搭配下面的範例程式觀看。

直接從程式看的話,這兩個範例執行結果是一樣的:

範例1

async function asyncExampleFunc1() {
  console.log('Hi, I will run first.');
  await asyncExampleFunc2();
  console.log('I need to wait...Orz');
}

async function asyncExampleFunc2() {
  console.log('I will run just like synchronously.');
}
asyncExampleFunc1();

console.log('I need to wait...Orz'); 可以當作 microtask,就如同 Promise.then()。

範例2

async function asyncExampleFunc1() {
  console.log('Hi, I will run first.');
  Promise.resolve(asyncExampleFunc2()).then(() => {
    console.log('I need to wait...Orz');
  })
}

async function asyncExampleFunc2() {
  console.log('I will run just like synchronously.');
}
asyncExampleFunc1();

從語法糖的角度看,await 就相當於 Promise 的 then,await 後面的程式碼會當作 Microtasks 存放到 Job Queue,而 try catch 語法則代替了 Promise 的 catch 做錯誤處理。


範例

接下來我們來看更多範例,讓自己更了解 async/await。

1. 基本範例

async await 常見搭配 try catch 語法去做錯誤處理

const getPosts = async () => {
  try {
    const posts = await getPostsApi(...);
    console.log(posts);
  } catch(err) {
    console.log(err);
  }
};

2. 用 async/await 依序處理多個非同步任務(Sequence)

function returnPromise(time) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(time), Math.random() * 5000);
  })
}

const randomTime = Math.floor(Math.random() * 5);

async function returnThreePromise() {
  try {
    const promise1value = await returnPromise(randomTime);
    console.log('myPromise1 resolve:', promise1value);

    const promise2value = await returnPromise(2);
    console.log('myPromise2 resolve:', promise2value);

    const promise3value = await returnPromise(3);
    console.log('myPromise3 resolve:', promise3value); 
  } catch(error) {
    console.log(error);
  }

}

returnThreePromise();

3. Promise.all()/Promise.race() + async/await 進行 Parallel & Race 處理

前面是使用 Promise 去處理 Promise.all()/Promise.race() 回傳的 Promise 物件,這次改寫用 async/await 處理:

function getPromise(i) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(i), Math.random() * 5000);
  })
}

const arr = [];

for (let i = 0; i < 5; i++) {
  arr.push(getPromise(i));
}

// 多個同時執行,所有 promise 被處理才被回傳
async function parallel() {
  const result = await Promise.all(arr);
  console.log(result); // [0, 1, 2, 3, 4]
}

// 最先完成的才被回傳
async function race() {
  const result = await Promise.race(arr);
  console.log(result); // 印出 0~4 其中一個數字
}

parallel();
race();

4. 練習題

這題目是從 Day19 的題目延伸而來,現在我們瞭解了 Promise & async/await,可以來小試身手一下!

問: 這段程式碼依序印出?(判斷程式執行順序)

async function asyncExampleFunc1() {
  console.log('Hi, I will run first.');
  await asyncExampleFunc2();
  console.log('I need to wait...Orz');
}

async function asyncExampleFunc2() {
  console.log('I will run just like synchronously.');
}

setTimeout(() => {
  console.log('I will be printed out first.');
}, 0);

asyncExampleFunc1();

const promiseExample = new Promise(function (resolve) {
  console.log('Promise executor function will run synchronously');
  resolve('But I still faster than you, Mr.SetTimeout. I am a microtask.')
}).then((data) => {
  console.log(data);
})

function mockFunc() {
  console.log('Shut up! I am a synchronous task so I will run first.');
}

mockFunc();

防雷-以下是執行結果

讀者也可以進去此 Codepen 查看執行結果

範例執行結果

如果 Day4 & 上篇文章和這篇文章都有讀完並了解的話,這題我想可以解的出來,若還不熟悉可以再重新閱讀一下!

Promise 和 async/await 就介紹到這邊啦~下一篇會將也可以做非同步處理但還沒有介紹到的 Generator 產生器做介紹。


更多練習

問底下程式碼的 console.log 印出什麼? 解答在留言區。

async function getData() {
  return await Promise.resolve('I made it!');
}

const data = getData();
console.log(data);

參考資料 & 推薦閱讀

[JS] Async and Await in JavaScript

一篇文章解决Promise...then,async/await执行顺序类型题


上一篇
Day19-JavaScript Promise 系列-更多關於 Promise 的練習
下一篇
Day21-非同步處理方式-Generator
系列文
強化 JavaScript 之 - 程式語感是可以磨練成就的30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
harry xie
iT邦研究生 1 級 ‧ 2024-02-16 17:22:03

練習題解答

印出 Promise {<pending>},async 函式會回傳一個 Promise 物件,而 await 會等待 Promise 進到 resolve 狀態,所以是 pending 狀態。

我要留言

立即登入留言