目錄
- 異步編程樣例
- 樣例決議
- 淺談Promise如何實作異步執行
- 參考
1.異步編程樣例
樣例:
// 等待執行函式
function sleep(timeout) {
return new Promise((resolve) => {
setTimeout(resolve, timeout)
})
}
// 異步函式
async function test() {
console.log('test start')
await sleep(1000)
console.log('test end')
}
console.log('start')
test()
console.log('end')
執行結果:
start
test start
end
test end
2.樣例決議
在樣例代碼中,test異步函式使用了async和await語法,這是ES2017里面的異步編程規范,而為了在較低版本的瀏覽器或Node支持這種語法,其中一種解決方案是將其轉化為Generator函式和Promise來實作,換句話說,任何的async和await實作的異步函式,都可以替換成Generator函式和Promise實作,
第一步:先將async和await語法替換為相應的Generator 函式,如下
// 代碼結構完全一致,只是替換了對應關鍵字
function *test() {
console.log('test start')
yield sleep(1000)
console.log('test end')
}
第二步:為了執行Generator 函式,使用Promise實作一個自動執行器函式 spawn
function spawn(genF) {
return new Promise(function(resolve, reject) {
const gen = genF();
function step(nextF) {
let next;
try {
next = nextF();
} catch(e) {
return reject(e);
}
if(next.done) {
return resolve(next.value);
}
Promise.resolve(next.value).then(function(v) {
step(function() { return gen.next(v); });
}, function(e) {
step(function() { return gen.throw(e); });
});
}
step(function() { return gen.next(undefined); });
});
}
第三步:將相應的Generator 函式和自動執行器函式相結合,即可得最終的等價代碼
function asyncTest() {
spawn(test)
}
console.log('start')
asyncTest()
console.log('end')
第四步:執行結果,與原來的一致
start
test start
end
test end
第五步:分析asyncTest函式執行程序
- 執行到
spawn(test),進入spawn函式中,創建一個Promise并回傳, - 執行到
const gen = genF(),獲取一個狀態機,(Generator 函式是一個狀態機,封裝了多個內部狀態,) - 執行到
step(function() { return gen.next(undefined); }), 進入step函式中, - 執行到
next = nextF(),next等于gen.next(undefined)的回傳結果,- 當
gen.next(undefined)開始執行,狀態機第一次呼叫,直到遇到第一個yield運算式為止,即yield sleep(1000),此時控制臺先輸出"test start",并且回傳第一個狀態{ value: reuslt, done: false }, 而 reuslt等于sleep(1000)回傳的結果,其是一個Promise,
- 當
- 執行到
if(next.done),此時第一個狀態的done為false,所以不執行if陳述句里面,繼續往下執行, - 執行到
Promise.resolve(next.value),由于第一個狀態的value是一個Promise,所以直接回傳其本身,也就相當于執行sleep(1000).then(...),sleep函式異步等待1秒后,resolve接受的值為undefined,繼續執行then方法, - 執行到
step(function() { return gen.next(v); }),此時v為undefined,再次進入step函式中, - 再次執行到
next = nextF(),next等于gen.next(undefined)的回傳結果,- 當
gen.next(undefined)再次執行時,狀態機第二次呼叫,此時Generator函式已經執行完畢,此時控制臺先輸出"test end",并且回傳最后的狀態{ value: undefined, done: true }
- 當
- 執行到
if(next.done),此時第一個狀態的done為true,所以執行if陳述句里面, - 執行到
return resolve(next.value),此時最初的Promise成功執行, - 至此
asyncTest函式執行結束,
3.淺談Promise如何實作異步執行
從上述樣例決議中可以看出,我們是用Promise來實作代碼的異步執行,那Promise的內部是如何實作異步執行的呢?
通過查看Promise的原始碼實作,發現其異步執行是通過asap這個庫來實作的,
asap是as soon as possible的簡稱,在Node和瀏覽器環境下,能將回呼函式以高優先級任務來執行(下一個事件回圈之前),即把任務放在微任務佇列中執行,
宏任務(macro-task)和微任務(micro-task)表示異步任務的兩種分類,在掛起任務時,JS 引擎會將所有任務按照類別分到這兩個佇列中,首先在 macrotask 的佇列(這個佇列也被叫做 task queue)中取出第一個任務,執行完畢后取出 microtask 佇列中的所有任務順序執行;之后再取 macrotask 任務,周而復始,直至兩個佇列的任務都取完,
用法:
asap(function () {
// ...
});
補充說明:這里提及的Promise原始碼并不是Node和瀏覽器的原生實作,是一個第三方庫,僅以此為參考,
4.參考
ECMAScript 6 入門 - async 函式
【翻譯】Promises/A+規范
Promise - Bare bones Promises/A+ implementation
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/127958.html
標籤:JavaScript
上一篇:函式之間可以相互呼叫
