日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

nodejs全局变量第一次没赋值要第二次才有是为什么_【NodeJS】async 和 await 的本质...

發布時間:2025/3/21 javascript 65 豆豆
生活随笔 收集整理的這篇文章主要介紹了 nodejs全局变量第一次没赋值要第二次才有是为什么_【NodeJS】async 和 await 的本质... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

對于許多新接觸 NodeJS 的人而言,async 和 await 的原理是比較難理解的。本文將從零“構建”出 async 和 await 關鍵字,從而幫助理清 async 和 await 的本質。

先用一句話概括:async 和 await 是內置了執行器的 generator 函數。

什么是 generator 函數?顧名思義,generator 函數就是一個生成器。生成的是一個可以多次通過 .next() 迭代的對象,例如,定義一個 generator 函數如下:

let g = function* () {yield 1yield 2return 3 }

其中,yield 關鍵字定義每次迭代的返回值,最后一個返回值用 return。

然后,就可以用它來生成一個可迭代的對象:

let iter = g() ? console.log(iter.next()) console.log(iter.next()) console.log(iter.next()) console.log(iter.next())

以上代碼執行的結果是:

{ value: 1, done: false } { value: 2, done: false } { value: 3, done: true } { value: undefined, done: true }

generator 函數也可以接收參數:

let g = function* (a, b) {yield ayield breturn a + b } ? let iter = g(1, 2) ? console.log(iter.next()) console.log(iter.next()) console.log(iter.next()) console.log(iter.next())

執行結果:

{ value: 1, done: false } { value: 2, done: false } { value: 3, done: true } { value: undefined, done: true }

接下來是一個關鍵點:yield 關鍵字的值和 .next() 的參數的關系:

let g = function* () {let ret = yield 1return ret } ? let iter = g() ? console.log(iter.next()) console.log(iter.next(2))

以上代碼的執行結果是:

{ value: 1, done: false } { value: 2, done: true }

可以看出,第二次調用 .next() 的時候,傳入了參數2,這個 2 被賦值給了 ret。也就是說,

? let ret = yield 1

這行代碼其實是被拆成兩段執行的。第一次調用 .next() 的時候,g 里面的代碼開始執行,執行到了 yield 1 這里,就暫停并返回了。這時打印 .next() 的返回值是 { value: 1, done: false }。然后,執行 .next(2) 的時候,又回到了 g 里面的代碼,從 let ret = 2 開始執行。

理清楚這一執行過程非常重要。因為,這意味著:

如果我在 g 里面 yield 一個 Promise 出去,在外面等 Promise 執行完之后,再通過 .next() 的參數把結果傳進來,會怎樣呢?

let asyncSum = function(a, b) {return new Promise(resolve => {setTimeout(() => {resolve(a + b)}, 1000)}) } ? let g = function* () {let ret = yield asyncSum(1, 2)console.log(ret)return ret } ? let iter = g() ? let p = iter.next().value p.then(sum => {iter.next(sum) })

執行結果就是等待一秒之后打印出3:

// 這里掛起了一秒鐘 3

請細細品味上面代碼里面的 g 函數:

let g = function* () {let ret = yield asyncSum(1, 2)console.log(ret)return ret }

將其與下面代碼進行對比:

let g = async function () {let ret = await asyncSum(1, 2)console.log(ret)return ret }

發現了吧?事實上 async 函數就是 generator 函數。

然而有人會問,我們調用 async 函數,都是直接調用,返回一個 Promise ,而不用像上面調用 g 那么麻煩的。

沒錯。上面調用 g 的代碼:

let iter = g() ? let p = iter.next().value p.then(sum => {iter.next(sum) })

叫做 g 的執行器。我們可以把它封裝起來:

let executor = function() {return new Promise(resolve => {let iter = g() ?let p = iter.next().valuep.then(sum => {let ret = iter.next(sum)resolve(ret.value)})}) } ? executor().then(ret => {console.log(ret) })

執行結果:

// 掛起一秒鐘 3 // g 里面的 console.log(ret) 3 // .then 里面的 console.log(ret)

實際上,node的執行引擎悄悄地幫我們做了上面的事情,當我們直接調用一個 async 函數時,其實是在調用它的執行器。

原理講到這里就完了。下面是擴展部分。

上面的 executor 函數是僅僅針對這個例子里面的 g 寫的。那我們是否可能寫一個通用的執行器函數,適用于任何 generator 函數呢?不管 generator 函數里面有多少個 yield ,這個執行器是否都可以自動全部處理完?

答案當然是肯定的,用到了遞歸,請看完整代碼:

let asyncSum = function(a, b) {return new Promise(resolve => {setTimeout(() => {resolve(a + b)}, 1000)}) ?} ? let asyncMul = function(a, b) {return new Promise(resolve => {setTimeout(() => {resolve(a * b)}, 1000)}) } ? let g = function* (a, b) {let sum = yield asyncSum(1, 2)let ret = yield asyncMul(sum, 2)return ret } ? function executor(generator, ...args) {let iter = generator.apply(this, args)let n = iter.next()if (n.done) {return new Promise(resolve => resolve(n.value))} else {return new Promise(resolve => {n.value.then(ret => {_r(iter, ret, resolve)});});} } ? function _r(iter, ret, resolve) {let n = iter.next(ret)if (n.done) {resolve(n.value)} else {n.value.then(ret => {_r(iter, ret, resolve)})} } ? executor(g, 1, 2).then(ret => {console.log(ret) })

執行結果:

// 這里掛起了兩秒鐘 6

不過上面這個 executor 是個不完善的版本,因為沒有考慮錯誤的情況。其實早在 async 和 await 還沒有出現的 2013 年,著名程序員 TJ Holowaychuk 就寫了一個完善的 generator 執行器。項目地址:https://github.com/tj/co 。其名字叫 co。典型用法就是:

co(function* () {var result = yield Promise.resolve(true);return result; }).then(function (value) {console.log(value); }, function (err) {console.error(err.stack); });

關于 async 和 await 的本質,到這里就結束了。文章最后請有興趣的讀者思考一個問題:為什么 TJ Holowaychuk 的這個模塊名字要叫做 co?

總結

以上是生活随笔為你收集整理的nodejs全局变量第一次没赋值要第二次才有是为什么_【NodeJS】async 和 await 的本质...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。