[JS] Async and Await in JavaScript
解決 JS 中 Async 問題的第一個方法是用 callback function,但為了避免 callback hell 後來我們可以使用 Promise,在 ES2017 正式制訂了 asynchronous functions,可以讓我們在寫非同步碼的程式時,如同在寫同步的程式碼一般。
async function getData () {
let resolveValue = await <Promise>
}
程式碼範例:
- Promise 和 Async/Await @ PJCHENder Gist
- Demo Async and Await @ JSFiddle
Use Async Await with Promise
觀念
AsyncFunction 物件和 Generator 非常相似,兩者的差別在於 AsyncFunction 會回傳 Promise 物件而不是 { value: any, done: Boolean }。
Async Function
async function getData() {
// ...
}
當 async function 被呼叫的時候,它會回傳一個
Promise。當 async function 回傳一個值,該 Promise 狀態會是 resolved 且帶有該回傳值。當 async function 拋出一個例外或某個值,該 Promise 狀態會是 rejected 且帶有該拋出的值。async function 內部可以使用
await表達式,它會暫停此 async function 的執行,並且等待傳遞至表達式的 Promise 的解析,解析完之後會回傳解析值,並繼續此 async function 的執行。From :Async Function @ MDN
Await Function
/**
* await 會暫停 async function 的執行,
* 等待 Promise 物件被 resolve 後再繼續 async function 的執行。
* 同時回傳被 resolve 的值。
**/
async function getData () {
let resolveValue = await <Promise>
}
如果回傳值不是一個 Promise 物件,則會被轉換為 resolved 狀態的 Promise 物件。 如果 Promise 物件被 rejected,則 await 會丟出 rejected 的值。 From :Await @ MDN
// async function 本身會回傳 Promise,
// 因此 async function 之後也可以用 `.then()` 串起來
main().then((value) => {
console.log(value);
});
async function main() {
console.log('start fn'); // STEP 1-1
let val = await get(); // STEP 1-2
console.log('get value after await', val); // STEP 2-1
let result = await process(val); // STEP 2-2
console.log('result', result); // STEP 3-1
return result;
}
function get() {
console.log('I am in get out of Promise'); // STEP 1-3
return new Promise((resolve, reject) => {
// STEP 1-4
setTimeout(() => resolve('secret'), 1000); // STEP 1-5 To STEP 2 after 1 sec
});
}
function process(value) {
console.log("I'm in process,", value); // STEP 2-3
return new Promise((resolve, reject) => {
// STEP 2-4
setTimeout(() => resolve(`${value}-secret`), 1000); // STEP 2-5 To STEP 3 after 1 sec
});
}
基本寫法
我們只需要在定義 function 前加上 async 就可以使用 async function
// Basic function
async function foo() { … }
// Arrow function
const foo = async () => { … }
// Class methods
class Bar {
async foo() { … }
}
在我們定義好 async function 後,我們就可以使用 await 這個關鍵字,這個關鍵字會放在 Promise 之前,直到這個 promise 處於 fulfilled 或 rejected 狀態後才會執行。每一個 async function 都會回傳一個 promise。
foo().then((res) => console.log(res)); // ‘success!’
async function foo() {
await somePromise();
return 'success!';
}
function somePromise() {
setTimeout(() => {
console.log('get data');
}, 1000);
}
錯誤處理
在 async 和 await 中的錯誤處理是使用 try...catch...:
async function foo() {
try {
const value = await somePromise();
return value;
} catch (err) {
console.log('Oops, there was an error :(');
}
}
callback function
main(); // STEP 1-1
function main() {
console.log('start run'); // STEP 1-2
get((value) => {
// STEP 1-3
console.log('value in get', value); // STEP 2-1
process(value, function (result) {
// STEP 2-2
console.log(result); // STEP 3-2
});
});
}
function get(callback) {
console.log('run fn get'); // STEP 1-4
return setTimeout(function () {
// STEP 1-5 into STEP 2
callback('get data'); // STEP 2 會在兩秒後執行
}, 2000);
}
function process(value, callback) {
console.log('value in process', value); // STEP 2-3
return setTimeout(function () {
// STEP 2-4 in to STEP 3
callback(`${value}-secrete`); // STEP 3-1 會在等 1 秒(共 3 秒)後執行
}, 1000);
}
/**
* main 執行 -> // start run(0sec)
* get(callback) ->
* 兩秒後執行 get 裡的 callback
* - 1. console.log(value in get get data) // value in get get data (2sec)
* - 2. process(value, callback) // value in process get data (2sec)
* 一秒後執行 process 裡的 callback
* - 1. console.log(`${value}-secrete`) // get data-secrete (3sec)
**/