溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Node.js中怎么使用Hooks實(shí)現(xiàn)異步

發(fā)布時(shí)間:2021-07-20 16:35:46 來源:億速云 閱讀:308 作者:Leah 欄目:web開發(fā)

本篇文章為大家展示了Node.js中怎么使用Hooks實(shí)現(xiàn)異步,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。

API使用

我總是覺得官方文檔過于復(fù)雜以及苛刻。這就是為什么我通常會選擇傳統(tǒng)、友好的博客文章。

讓我們首先了解一下Async Hooks API提供的 5 個(gè)可用事件函數(shù):

  •  init: 顧名思義,當(dāng)特定的異步資源初始化時(shí)會調(diào)用它。僅作記錄,此時(shí),我們已經(jīng)將鉤子與異步資源相關(guān)聯(lián)。

  •  before 和 after: 這與普通語言中的函數(shù)的執(zhí)行前和執(zhí)行后非常相似。在資源執(zhí)行之前和之后分別調(diào)用它們。

  •  destroy: 很明顯,無論資源的回調(diào)函數(shù)發(fā)生了什么,只要資源被銷毀就會調(diào)用它。

  •  promiseResolve: promiseResolve與Promise有關(guān),當(dāng)你的Promise調(diào)用它的 resolve 函數(shù)時(shí),掛鉤就會觸發(fā)此函數(shù)。

非常的簡單直接,接下來讓我們看一個(gè)基本的例子:

const myFirstAsyncHook = async_hooks.createHook({ init, before, after, destroy, promiseResolve });

是的,你必須先創(chuàng)建每個(gè)事件函數(shù),然后再將其分配給createHook函數(shù)。另外,必須顯式啟用該掛鉤:

myFirstAsyncHook.enable();

讓我們繼續(xù)看一個(gè)更加完整的例子:

const fs = require("fs");  const async_hooks = require("async_hooks");  // Sync write to the console  const writeSomething = (phase, more) => {     fs.writeSync(        1,        `Phase: "${phase}", Exec. Id: ${async_hooks.executionAsyncId()} ${              more ? ", " + more : ""          }\n`     ); };  // Create and enable the hook  const timeoutHook = async_hooks.createHook({     init(asyncId, type, triggerAsyncId) {        writeSomething(           "Init",           `asyncId: ${asyncId}, type: "${type}", triggerAsyncId: ${triggerAsyncId}`       );     },     before(asyncId) {        writeSomething("Before", `asyncId: ${asyncId}`);     },     destroy(asyncId) {        writeSomething("Destroy", `asyncId: ${asyncId}`);     },     after(asyncId) {        writeSomething("After", `asyncId: ${asyncId}`);     },  });  timeoutHook.enable();  writeSomething("Before call");  // Set the timeout  setTimeout(() => {     writeSomething("Exec. Timeout");  }, 1000);

這個(gè)例子通過眾所周知的原生函數(shù) setTimeout 去追蹤超時(shí)的異步執(zhí)行過程。

在我們深入研究之前,先快速瀏覽一下第一個(gè)函數(shù) writeSomething 。你也許很好奇為什么在我們已經(jīng)有函數(shù)可以在控制臺輸出的情況下仍然創(chuàng)建了一個(gè)新的函數(shù)去完成相同的功能。

原因是你不能使用任何 console 函數(shù)去測試異步鉤子,因?yàn)樗鼈儽旧砭褪钱惒降摹R虼水?dāng)我們在下面提供了一個(gè) init 函數(shù)時(shí),它會產(chǎn)生一個(gè)無限循環(huán)。該函數(shù)會調(diào)用 console 的 log ,此日志又會再次觸發(fā)初始化,以此類推,陷入死循環(huán)。

這就是為什么我們需要重新寫一個(gè)“同步”日志功能。

好了,現(xiàn)在我們回過頭去看代碼。我們的異步鉤子提供了四個(gè)功能:init、 before、 after 以及 destory。而且,我們還在超時(shí)之前和執(zhí)行期間打印一條消息,所以你可以看到整個(gè)過程是如何線性進(jìn)行的。

在你的命令行執(zhí)行 node index.js,你會得到如下圖所示的結(jié)果:

Node.js中怎么使用Hooks實(shí)現(xiàn)異步

觀察下鉤子是如何一步一步執(zhí)行追蹤的??雌饋硎且环N很有趣的跟蹤方式,尤其是當(dāng)你考慮將數(shù)據(jù)輸入到監(jiān)視工具中或者是你已經(jīng)使用的日志追蹤工具。

一個(gè)Promise例子

讓我們看看我們的示例在Promise下的執(zhí)行效果。思考下面這些代碼片段:

const calcPow = async(n, exp) => {     writeSomething("Exec. Promise");     return Math.pow(n, exp);  };  (async() => {     await calcPow(3, 4);  })();

你也可以用之前的 setTimeout 示例來替代這個(gè)例子。在這段代碼中,我們有一個(gè)異步函數(shù)用來進(jìn)行冪運(yùn)算。同時(shí)也有一個(gè)相同的函數(shù)在異步塊中被調(diào)用。到目前為止,Node.js創(chuàng)建了兩個(gè)Promise。

下圖是日志記錄的結(jié)果:

Node.js中怎么使用Hooks實(shí)現(xiàn)異步

奇怪的是,我們有兩個(gè)Promise,卻調(diào)用了三次 init 函數(shù)。不用擔(dān)心,這是因?yàn)镹ode.js團(tuán)隊(duì)在版本12中引入了異步執(zhí)行性能方面的一些最新改進(jìn)。你可以點(diǎn)擊此處[4]了解更多信息。

盡管如此,執(zhí)行過程依然符合我們的預(yù)期。

解析:鉤子函數(shù)的性能與度量

Node.js提供的另一個(gè)非常有趣的API是性能評估API[5],既然我們在這里討論度量,為什么不結(jié)合兩者的功能來了解我們可以收獲什么呢?

可以通過 perf_hooks 獲得該API,該API讓我們能夠用與W3C Web Performance API[6]相似的方式來獲得性能/用戶時(shí)間軸指標(biāo)。

將它與異步鉤子相結(jié)合我們可以做一些事情,比如追蹤異步函數(shù)執(zhí)行完畢需要的時(shí)間。讓我們看另外一個(gè)例子:

const async_hooks = require("async_hooks");  const {     performance,     PerformanceObserver  } = require("perf_hooks");  const hook = async_hooks.createHook({     init(asyncId) {        performance.mark(`init-${asyncId}`);     },     destroy(asyncId) {        performance.mark(`destroy-${asyncId}`);        performance.measure(           `entry-${asyncId}`,           `init-${asyncId}`,           `destroy-${asyncId}`        );     },  });  hook.enable();  const observer = new PerformanceObserver((data) =>     console.log(data.getEntries())  );  observer.observe({     entryTypes: ["measure"],     buffered: true  });  setTimeout(() => {     console.log("I'm a timeout");  }, 1200);

既然我們只是追蹤記錄執(zhí)行時(shí)間,就沒有必要用之前用的中間事件函數(shù)。用 init 和 destroy 就足夠了。

就像異步鉤子那樣,性能API通過創(chuàng)建觀察者來工作。不過,無論什么時(shí)候開始或者結(jié)束,你都必須明確標(biāo)記每個(gè)事件的id。這樣,當(dāng)我們調(diào)用API的 measure 函數(shù)時(shí),它將匯總收集到的數(shù)據(jù)并將其立即發(fā)送給觀察者,觀察者將為我們記錄全部的日志。

注意了,這里我們使用了兩次 console.log 函數(shù)。第一次是無影響的因?yàn)樗谟^察者中執(zhí)行。但是第二次它在 setTimeout 函數(shù)中執(zhí)行,另一個(gè)異步中的異步,這意味著在最后它會產(chǎn)生不同的輸出。

下圖是日志記錄:

Node.js中怎么使用Hooks實(shí)現(xiàn)異步

本示例本并沒有考慮事件類型之間的差異。在這里,我們在同一測量場景中發(fā)生了超時(shí)和異步日志操作。

但是,考慮到生產(chǎn)環(huán)境,建議你創(chuàng)建一個(gè)更強(qiáng)大的機(jī)制在每次調(diào)用 init 時(shí)存儲事件類型,并在稍后調(diào)用 destroy 函數(shù),倒霉的沒有接收到參數(shù)類型時(shí)檢查存儲是否依然存在。

異步資源

Async Hooks中的另一個(gè)有用功能是 `AsyncResource`[7] 類。每當(dāng)你為框架或庫創(chuàng)建自己的資源時(shí),它都會為你提供幫助。

只需輸入以下代碼即可使用:

const AsyncResource = require('async_hooks').AsyncResource;

用這種方式,你可以使用它實(shí)例化一個(gè)新對象,并手動(dòng)定義其每個(gè)階段在整個(gè)代碼中何時(shí)開始。舉個(gè)例子:

const resource = new AsyncResource('MyOwnResource');  someFunction(function someCallback() {     resource.emitBefore();     // do your stuff...     resource.emitAfter();  });  someOnClose() {     resource.emitDestroy();  }

上述內(nèi)容就是Node.js中怎么使用Hooks實(shí)現(xiàn)異步,你們學(xué)到知識或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識儲備,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI