[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)
**/