眾所周知,async/await只是一個語法糖,它是基於生成器來實現的,我根據網上的資料,從頭開始寫出它中間的原理實現。
生成器
生成器是在定義函數時在function后添加*定義的,像這樣:function* func(){}
,執行生成器函數后會得到一個迭代器,在生成器函數中能支持yield
來暫停函數,直到迭代器調用next
方法.同時next
能傳入一個參數來作為yield的值。
這里先定義兩個異步函數來作為以后的實驗對象:
function afunc() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("afunc");
}, 1000);
});
}
function bfunc() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("bfunc");
}, 2000);
});
}
第一版
首先分別定義兩個函數,一個是收集在async函數中需要使用的異步函數,一個是async函數主體在這里運行異步的函數。
function collector(...fn) {
return function () {
for (const item of fn) {
item().then((fulfilled) => it.next(fulfilled));
}
};
}
function* async(...fn) {
let inner = collector(...fn);
console.log("aaa");
console.log(yield inner());
console.log("bbb");
console.log(yield inner());
}
let it = async(afunc, bfunc);
it.next();
// 運行結果: aaa afunc bbb bfunc
可以看到,已經可以讓同步方法在兩個異步函數中間了,但是這個方案有個問題,本來這個函數期待是三秒打印完,這里只花了兩秒,原因在於在collector
中同步進行了數組遍歷,所以需要把。
第二版
既然原因是同步執行了收集到的函數,那我直接寫在函數體里不就行了?這樣就可以得到這個並不好用的第二版:
function* generator() {
console.log("aaa");
let result = yield afunc().then((fulfilled) => {
it.next(fulfilled);
});
console.log(result);
let other = yield bfunc().then((fulfilled) => {
it.next(fulfilled);
});
console.log(other);
}
it = generator();
it.next();
// 運行結果: aaa afunc bbb bfunc
不過既然已經得到了想要的結果,這里只需要把它包裝一下就可以了
第三版
這一版就相當接近async/await的寫法了,通過在生成器函數中定義函數執行順序,在每次執行異步函數時跳出,等到異步函數執行完成后再進行下一步。
function* generator() {
let result = yield afunc();
console.log(result);
let other = yield bfunc();
console.log(other);
}
myAwait(generator);
function myAwait(genner, ...args) {
let iter = genner(...args); //得到生成器的迭代器
return new Promise((resolve, reject) => {
let result; //iter每次暫停時的結果
//! inner就是在手動迭代iter
let inner = function (yield) {
result = iter.next(yield); //開始迭代 將這里的yield當作yield傳入生成器
if (result.done) {
//迭代結束:
resolve(result.value); //Promise結束
} else {
//如果沒有結束 等到promise的結束繼續遞歸
return Promise.resolve(result.value).then((fulfilled) => {
inner(fulfilled);
});
}
};
inner(); //迭代器第一次不應該傳入參數
});
}
只要再加上一些錯誤處理,這樣一個手寫async/await就完成了。