async 和 await 幾乎是 nodejs 的最典型關鍵字,最能代表 nodejs 的特色,然而掌握這兩個關鍵字的原理卻不容易。這篇文章使用從零「構建」出 async 和 await 關鍵字的方式,來幫助理清 async 和 await 的本質。
先用一句話概括:async 和 await 是內建了執行器的 generator 函式。
什麼是 generator 函式?顧名思義,generator 函式就是乙個生成器。生成的是乙個可以多次通過 .next() 迭代的物件,例如,定義乙個 generator 函式如下:
let g = function*()
其中,yield 關鍵字定義每次迭代的返回值,最後乙個返回值用 return。
然後,就可以用它來生成乙個可迭代的物件:
let iter =g()console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
以上**執行的結果是:
generator 函式也可以接收引數:
let g = function*(a, b)let iter = g(1, 2)
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
執行結果:
接下來是乙個關鍵點:前面的例子中,呼叫next() 時並沒有傳遞引數,但是實際上 next() 是可以接受引數的,而且這個引數和 yield 關鍵字有特殊的關係:
let g = function*()let iter =g()
console.log(iter.next())
console.log(iter.next(2))
以上**的執行結果是:
可以看到,next(2) 這個呼叫導致 ret 的值變成了2。這是為什麼呢?因為 next() 的引數會成為 yield 表示式的值。也就是說,
let ret = yield 1
這行**其實是被拆成兩段執行的。第一次呼叫 .next() 的時候,執行到了 yield 1 這裡,就暫停並返回了。這時列印 .next() 的返回值是 。然後,執行 .next(2) 的時候,又回到了 g 裡面的**,從 let ret = 2 開始執行。
理清楚這一執行過程非常重要。因為,這意味著:
如果我在 g 裡面 yield 乙個 promise 出去,在外面等 promise 執行完之後,再通過 .next() 的引數把結果傳進來,會怎樣呢?
let asyncsum = function(a, b) , 1000)
})}
let g = function*()
let iter =g()
let p =iter.next().value
p.then(sum =>)
執行結果就是等待一秒之後列印出3:
//這裡掛起了一秒鐘
請細細品味上面**裡面的 g 函式:
let g = function*()
將其與下面**進行對比:
let g = async function()
是不是特別相似?事實上, async 函式的本質就是 generator 函式,只不過附帶了乙個執行器。
這裡就引出了執行器的概念。什麼叫執行器?讓我們回到 g:
let g = function*()
g 作為乙個 generator 函式,是有兩段的。如果我們想要把 g 從頭到尾執行完,需要這樣子呼叫 g:
let iter =g()let p =iter.next().value // 第一次呼叫 next(),先執行第一段
p.then(sum =>)
這樣就特別麻煩,最好有乙個函式 executor(),我們可以把 g 當作引數傳給它,它會自動把 g 執行完,再把最後的結果返回回來。就像這樣:
executor(g).then(result => )
有沒有這樣的函式呢?有的,我們把上面那段**自己封裝一下,就可以自己寫出乙個 executor 來:
let executor = function(g) )
})}
executor().then(ret =>)
這個 executor 就叫做 g 的執行器。當然啦,這個執行器只是適用於 g,不夠通用,能不能做得更通用一點,使它能執行任何的 generator 函式呢?可以的,如下:
let asyncsum = function(a, b) , 1000)
})}
let asyncmul = function
(a, b) , 1000)
})}
let g = function*(a, b)
function
executor(generator, ...args)
else
); });
}}function
_r(iter, ret, resolve)
else
) }}
executor(g, 1, 2).then(ret =>)
執行結果:
//這裡掛起了兩秒鐘
6
co(function*() ).then(function
(value) ,
function
(err) );
所以 async 函式本質上就是內建了執行器的 generator 函式,只不過 nodejs 引擎幫我們實現了執行器。當我們呼叫 async 函式時,引擎內部呼叫了執行器。
原理到這裡就結束了。不過可能有細心的讀者發現乙個奇怪的現象:為什麼 tj holowaychuk 的這個模組名字要叫做 co?
答案是 co 代表 coroutine,也就是協程啦。理解到這裡就又更深入一層了,但是這裡不展開啦,async 函式是協程在 nodejs 中的實現形式。
async和await的講解
普通的函式宣告 async function a 複製 宣告乙個函式表示式 let a async function 複製 async形式的箭頭函式 let a async 複製 async與await例項應用,基礎 控制器呼叫與server中查詢資料 exports.getbloglist asy...
async和await的使用
async其實是es7的才有的,是非同步操作的進化,其實就是封裝乙個promise的物件返回 async function test console.log test promiseasync方法在普通的函式前加上 async 關鍵字即可。執行這個函式,發現並沒有返回1111,而是通過promise...
async和await的講解
async和await的講解 宣告async函式的幾個方法 普通的函式宣告 async function a 宣告乙個函式表示式 let a async function async形式的箭頭函式 let a async 初識async和await async與await例項應用,基礎 控制器呼叫與...