目錄 非同步編程樣例 樣例解析 淺談Promise如何實現非同步執行 參考 1.非同步編程樣例 樣例: 執行結果: 2.樣例解析 在樣例代碼中, 非同步函數使用了 和`await Generator Promise async await Generator Promise`實現。 第一步:先將 和`awa ...
目錄
- 非同步編程樣例
- 樣例解析
- 淺談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 () {
// ...
});
4.參考
Promise - Bare bones Promises/A+ implementation