JavaScript - Async/Await




Async functions

在上一篇中我們提到了 Promise ,避免使用多層 callback 所造成程式碼難以維護的問題。而 ES7 提供了 async/await 語法,可以看成是 Promise 的語法糖,更方便的寫出非同步程式碼。

async 所宣告的函式,會回傳一個 Promise ,使用方法就是在宣告的函式前增加 async 關鍵字,說明這是一個非同步的函式。

下面的範例相當於回傳一個 resolved promise:

1
2
3
async function f() {
return 1;
}

也可以寫成這樣:

1
2
3
async function f() {
return Promise.resolve(1);
}

Await

await 只能在 async 函式中使用,否則會拋出 syntax error 。 await 表示等待 Promise 的狀態為 resolve 或 reject 才會繼續執行函式,讓我們看一下例子:

1
2
3
4
5
6
7
8
9
10
11
12
async function main() {

let p = new Promise((resolve, reject) => {
setTimeout(() => resolve("done!"), 1000)
});

let result = await p; // 等待 promise resolves
console.log(result); // "done!"

}

main();

範例中的程式會等待 p 一秒鐘後才會進行輸出。

下面為一個簡易的 async/await 例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function p(value) {
return new Promise((resolve, reject) => {
setTimeout(function() {
value++
resolve(value)
}, 1000)
})
}

async function main() {
let x1 = await p(1);
let x2 = await p(2);
let x3 = await 3;

console.log(x1 + x2 + x3); // 8
}

main();

因為 async 函式相當於回傳一個 Promise ,所以我們一樣可以用操作 Promise Chain 的方式,把 Promise 一層一層的往下傳:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
async function main() {
console.log("Start main function" );
let x1 = await p1(1); // 2
let x2 = await p2(2); // 4
let x3 = await 3; // 3
await p2(4); // 6
return x1 + x2; // 6
}

main().then(value => {
return value + 2 ; // 8
}).then(p1)
.then((value) => {
console.log(value); // 9
}).catch((error) => {
console.log('error:', error)
})

function p1(value) {
console.log("P1 value is :" + value);
return new Promise((resolve, reject) => {
setTimeout(function() {
value++
resolve(value)
}, 1000)
})
}

function p2(value) {
console.log("P2 value is :" + value);
return new Promise((resolve, reject) => {
setTimeout(function() {
value+=2
resolve(value)
}, 1000)
})
}

/*Output:
Start main function
P1 value is :1
P2 value is :2
P2 value is :4
P1 value is :8
9
/*

await 可以搭配 Promise.all 和 Promise.race 來使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
async function main() {
console.log("Start main function" );
const [x1, x2] = await Promise.all([p(1000), p(2000)]);
const x3 = await Promise.race([p(1000), p(2000), p(3000)]);
return x1 + x2 + x3; // 4000
}

main().then(value => {
console.log(`value is ${value}`);
})

function p(value) {
return new Promise((resolve, reject) => {
setTimeout(function() {
if (value < 1000 ){
        reject('Unexpected condition')
}
resolve(value)
}, value)
})
}

錯誤處理

錯誤處理的方式使用 try / catch :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const isEven = (num) => {
return new Promise((resolve, reject) => {
if (num % 2 == 0) {
resolve('even');
} else {
reject('odd');
}
});
}

const verify = async (num) => {
try {
const sign = await isEven(num);
console.log(sign);
} catch (err) {
console.log(err);
}
}

verify(8); // even
verify(15); // odd
verify('Ian'); // odd

只用 catch 也行:

1
2
3
4
5
6
const verify = async (num) => {
const sign = await isEven(num);
console.log(sign);
}

verify('Ian').catch(err => console.log(err)); // odd

如果是採用以下的寫法,因為 await isEven(15) 的結果會是 reject ,所以在 x2 就會進入到 catch ,表示後面的 x3 就不會執行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const isEven = (num) => {
return new Promise((resolve, reject) => {
if (num % 2 == 0) {
resolve('even');
} else {
reject('odd');
}
});
}

const verify = async () => {
const x1 = await isEven(8);
const x2 = await isEven(15);
const x3 = await isEven('Ian');
return x1
}

verify()
.then((value) => {
console.log(value)
})
.catch((error) => {
console.log('error:', error)
})

async/await and promise.then/catch

透過在 await 我們就可以很容易的控制非同步執行, 再搭配 try / catch 來處理例外狀況,因為在全域環境中無法使用 await ,所以這時候就可以透過 .then / catch 來控制整個流程。

參考文獻

  1. [JS] Async and Await in JavaScript | PJCHENder 私房菜
  2. JS 原力覺醒 Day16 - Async / Await:Promise 語法糖
  3. Day25 優雅的 Await、Async
  4. Async/await
  5. How to use async/await in JavaScript