ES(2017)學習筆記(十)【Async】

風靈使發表於2019-04-06

1. 概述

async 函式是什麼?一句話,它就是 Generator 函式的語法糖。

2. 基本用法

2.1 返回 promise 物件

async 函式返回的是一個 promise 物件.

async function test (){
  return 'hello async';
}
let result = test();
console.log(result);
// Promise {<resolved>: "hello async"}

所以,async 函式返回的是一個 Promise 物件。從文件中也可以得到這個資訊。async 函式(包含函式語句、函式表示式、Lambda表示式)會返回一個 Promise 物件,如果在函式中 return 一個直接量,async 會把這個直接量通過 Promise.resolve() 封裝成 Promise 物件。所以 可以使用 then() 鏈來處理這個 Promise 物件,就像這樣:

test().then(v => {
  console.log(v); // hello async
});

2.2 Promise 物件的狀態變化

async 函式返回的 Promise 物件,必須等到內部所有 await 命令後面的 Promise 物件執行完,才會發生狀態改變,除非遇到 return 語句或者丟擲錯誤。也就是說,只有 async 函式內部的非同步操作執行完,才會執行 then 方法指定的回撥函式。

2.3 await 命令

await 操作符用於等待一個 Promise 物件,await 的使用語法非常簡單:

[return_value] = await expression;

其中 expression 是一個 Promise 物件或者任何要等待的值;

await expression 的執行結果有以下幾種情況:

  1. expression 是一個 Promise 物件, 並且其以值 xfulfilled, 則返回值為 x.
  2. expression 是一個 Promise 物件, 並且其以異常 erejected, 則丟擲異常 e.
  3. expression 不是 Promise 物件, 則會將 expression 處理成一個以 expression 值被 fulfilledPromise 物件, 然後返回這個 Promise 物件的最終值 (即 expression 值). 這種用法沒太大意義, 因此實際使用時還是儘量在 await 後跟一個 Promise 物件.

正常情況下,await 命令後面是一個 Promise 物件。如果不是,會被轉成一個立即 resolvePromise 物件。

async function f() {
  return await 123;
}

f().then(v => console.log(v))
// 123

上面程式碼中,await 命令的引數是數值123,它被轉成 Promise 物件,並立即 resolve

await 命令後面的 Promise 物件如果變為 reject 狀態,則 reject 的引數會被 catch 方法的回撥函式接收到。

async function f() {
  await Promise.reject('出錯了');
}

f()
.then(v => console.log(v))
.catch(e => console.log(e))
// 出錯了

注意,上面程式碼中,await 語句前面沒有 return,但是 reject 方法的引數依然傳入了 catch 方法的回撥函式。這裡如果在 await 前面加上 return,效果是一樣的。

只要一個 await 語句後面的 Promise 變為 reject,那麼整個 async 函式都會中斷執行。

async function f() {
  await Promise.reject('出錯了');
  await Promise.resolve('hello world'); // 不會執行
}

上面程式碼中,第二個 await 語句是不會執行的,因為第一個 await 語句狀態變成了 reject

有時,我們希望即使前一個非同步操作失敗,也不要中斷後面的非同步操作。這時可以將第一個 await 放在 try...catch 結構裡面,這樣不管這個非同步操作是否成功,第二個 await 都會執行。

async function f() {
  try {
    await Promise.reject('出錯了');
  } catch(e) {
  }
  return await Promise.resolve('hello world');
}

f()
.then(v => console.log(v))
// hello world

如果有多個 await 命令,可以統一放在 try...catch 結構中。

async function main() {
  try {
    const val1 = await firstStep();
    const val2 = await secondStep(val1);
    const val3 = await thirdStep(val1, val2);

    console.log('Final: ', val3);
  }
  catch (err) {
    console.error(err);
  }
}

相關文章