JavaScript ES8(ES2017)的版本支援 async/await 語法,就像這樣:
這個語法對於主力是 C# 的我們並不陌生,async/await 語法可以在該進行等候資源的時候進行等候,而且不會打亂我們閱讀程式碼的順序,清晰度大大提昇,不過我很好奇,它是怎麼辦到的?
爬了幾篇文章之後,了解到,說穿了其實 ES8 的 async/await 是 Generator Function + Promise 的語法糖,而這兩個東西是從 ES6(ES2017)加進來的,所以我們可以用 Generator Function + Promise 在 ES6 實現 async/await 的效果。
什麼是 Generator Function?
我們先看一下它的語法:
function* test() {
let a = yield getA();
console.log(a);
let b = yield getB();
console.log(b);
}
function*
即是將 Function 宣告為 Generator Function,用 yield
來暫停工作,將執行權交出去,進行等候,這是最關鍵的地方,而呼叫端則用 next()
方法來控制往下執行,像這樣:
let testFunc = test();
testFunc.next();
testFunc.next();
而且它這種特性,可以拿來做狀態機,更詳細的 Generator Function 介紹,可以參考這篇文章「Generator 函數的含義與用法」。
什麼是 Promise?
Promise 的出現解決了 Callback Hell 的麻煩,任何有延遲結果的 Function 都可以用 Promise 加以封裝,像 Fetch API 回傳的就是 Promise,底下是一個簡單的 Promise 範例:
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(100);
}, 500);
});
即使沒有延遲結果的 Function,如果為了統一介面,也可以用 Promise 封裝起來。
let promise = new Promise((resolve, reject) => {
const sum = 50 + 50;
resolve(sum);
});
然後接 then()
方法就能在 Promise 得到 resolve 或 reject 的結果之後,接續執行處理的邏輯。
promise.then(result => {
console.log(result);
}, reason => {
console.log(reason);
});
而且狀態是會保存起來的,即使在這之後的任意時間點呼叫 then() 方法,依舊能行。
setTimeout(() => {
promise.then(result => {
console.log(result);
}, reason => {
console.log(reason);
});
}, 1000);
更詳細的 Promise 介紹,可以參考這篇文章「你懂 JavaScript 嗎?#24 Promise」。
改寫吧
所以文章開頭的那個範例,我們可以改寫成這樣:
function* sum(integers) {
let _sum = 0;
for (let i = 0; i < integers.length; i++) {
let num = integers[i];
if (num === 5) {
let response = yield fetch("/Test/Data");
let data = yield response.json();
num = data.num;
}
_sum += num;
}
return Promise.resolve(_sum);
}
當我們確信 Generator Function 的 yield 個數時,就可以配合 yield 的個數呼叫固定次數的 next() 方法來執行。
const genSum = sum([1, 2, 3, 4, 5, 6, 7, 8, 9]);
genSum.next().value
.then(response => genSum.next(response).value)
.then(data => genSum.next(data).value)
.then(result => {
console.log(result);
});
但是這樣寫其實沒有什麼彈性,邏輯一改就爆了,為此我們可以為 Generator Function 做一個執行器,讓它自動往下執行。
function genSumRunner(integers) {
const genSum = sum(integers);
function next(data) {
const result = genSum.next(data);
if (result.done) return result.value;
return result.value.then(data => next(data));
}
return next();
}
genSumRunner([1, 2, 3, 4, 5, 6, 7, 8, 9]).then(result => {
console.log(result);
});
以上,就給各位朋友做參考,希望大家都能在前端的世界活得好好的。