一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

node.js|vue.js|jquery|angularjs|React|json|js教程|

服務器之家 - 編程語言 - JavaScript - node.js - Node8中AsyncHooks異步生命周期

Node8中AsyncHooks異步生命周期

2022-03-02 16:35隱冬 node.js

這篇文章主要介紹了Node8中AsyncHooks異步生命周期,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

Async Hooks 是 Node8 新出來的特性,提供了一些 API 用于跟蹤 NodeJs 中的異步資源的生命周期,屬于 NodeJs 內置模塊,可以直接引用。

?
1
const async_hooks = require('async_hooks');

這是一個很少使用的模塊,為什么會有這個模塊呢?

我們都知道,JavaScript在設計之初就是一門單線程語言,這和他的設計初衷有關,最初的JavaScript僅僅是用來進行頁面的表單校驗,在低網速時代降低用戶等待服務器響應的時間成本。隨著Web前端技術的發展,雖然前端功能越來越強大,越來越被重視,但是單線程似乎也沒有什么解決不了的問題,相比較而言多線程似乎更加的復雜,所以單線程依舊被沿用至今。

既然JavaScript是單線程,但是在日常開發中總是會有一些比較耗時的任務,比如說定時器,再比如說如今已經標準化的Ajax,JavaScript為了解決這些問題,將自身分為了BOM,DOM,ECMAScript,BOM會幫我們解決這些耗時的任務,稱之為異步任務。

正因為瀏覽器的BOM幫我們處理了異步任務,所以大部分的程序員對異步任務除了會用幾乎一無所知,比如同時有多少異步任務在隊列中?異步是否擁堵等,我們都是沒有辦法直接獲得相關信息的,很多情況下,底層確實也不需要我們關注相關的信息,但如果我們在某些情況下想要相關信息的時候,NodeJS提供了一個Experimental的API供我們使用,也就是async_hooks。為什么是NodeJS呢,因為只有在Node中定時器,http這些異步模塊,才是開發者可以控制的,瀏覽器中的BOM是不被開發者控制的,除非瀏覽器提供對應的API。

async_hooks規則

async_hooks約定每一個函數都會提供一個上下文,我們稱之為async scope,每一個async scope中都有一個 asyncId, 是當前async scope的標志,同一個的async scope中asyncId必然相同。

這在多個異步任務并行的時候,asyncId可以使我們可以很好的區分要監聽的是哪一個異步任務。

asyncId是一個自增的不重復的正整數,程序的第一個asyncId必然是1。

async scope通俗點來說就是一個不能中斷的同步任務,只要是不能中斷的,無論多長的代碼都共用一個asyncId,但如果中間是可以中斷的,比如是回調,比如中間有await,都會創建一個新的異步上下文,也會有一個新的asyncId。

每一個async scope中都有一個triggerAsyncId表示當前函數是由那個async scope觸發生成的;

通過 asyncId 和 triggerAsyncId 我們可以很方便的追蹤整個異步的調用關系及鏈路。

async_hooks.executionAsyncId()用于獲取asyncId,可以看到全局的asyncId是1。

async_hooks.triggerAsyncId()用于獲取triggerAsyncId,目前值為0。

?
1
2
3
const async_hooks = require('async_hooks');
console.log('asyncId:', async_hooks.executionAsyncId()); // asyncId: 1
console.log('triggerAsyncId:', async_hooks.triggerAsyncId()); // triggerAsyncId: 0

我們這里使用fs.open打開一個文件,可以發現fs.open的asyncId是7,而fs.open的triggerAsyncId變成了1,這是因為fs.open是由全局調用觸發的,全局的asyncId是1。

?
1
2
3
4
5
6
7
8
const async_hooks = require('async_hooks');
console.log('asyncId:', async_hooks.executionAsyncId()); // asyncId: 1
console.log('triggerAsyncId:', async_hooks.triggerAsyncId()); // triggerAsyncId: 0
const fs = require('fs');
fs.open('./test.js', 'r', (err, fd) => {
    console.log('fs.open.asyncId:', async_hooks.executionAsyncId()); // 7
    console.log('fs.open.triggerAsyncId:', async_hooks.triggerAsyncId()); // 1
});

異步函數的生命周期

當然實際應用中的async_hooks并不是這樣使用的,他正確的用法是在所有異步任務創建、執行前、執行后、銷毀后,觸發回調,所有回調會傳入asyncId。

我們可以使用async_hooks.createHook來創建一個異步資源的鉤子,這個鉤子接收一個對象作為參數來注冊一些關于異步資源生命周期中可能發生事件的回調函數。每當異步資源被創建/執行/銷毀時這些鉤子函數會被觸發。

?
1
2
3
4
5
6
const async_hooks = require('async_hooks');
 
const asyncHook = async_hooks.createHook({
  init(asyncId, type, triggerAsyncId, resource) { },
  destroy(asyncId) { }
})

目前 createHook 函數可以接受五類 Hook Callbacks 如下:

1.init(asyncId, type, triggerAsyncId, resource)

  • init 回調函數一般在異步資源初始化的時候被觸發。
  • asyncId: 每一個異步資源都會生成一個唯一性標志
  • type: 異步資源的類型,一般都是資源的構造函數的名字。

FSEVENTWRAP, FSREQCALLBACK, GETADDRINFOREQWRAP, GETNAMEINFOREQWRAP, HTTPINCOMINGMESSAGE,
HTTPCLIENTREQUEST, JSSTREAM, PIPECONNECTWRAP, PIPEWRAP, PROCESSWRAP, QUERYWRAP,
SHUTDOWNWRAP, SIGNALWRAP, STATWATCHER, TCPCONNECTWRAP, TCPSERVERWRAP, TCPWRAP,
TTYWRAP, UDPSENDWRAP, UDPWRAP, WRITEWRAP, ZLIB, SSLCONNECTION, PBKDF2REQUEST,
RANDOMBYTESREQUEST, TLSWRAP, Microtask, Timeout, Immediate, TickObject

  • triggerAsyncId: 表示觸發當前異步資源被創建的對應的 async scope 的 asyncId
  • resource: 代表被初始化的異步資源對象

我們可以通過 async_hooks.createHook 函數來注冊關于每個異步資源在生命周期中發生的 init/before/after/destory/promiseResolve 等相關事件的監聽函數;
同一個 async scope 可能會被調用及執行多次,不管執行多少次,其 asyncId 必然相同,通過監聽函數,我們很方便追蹤其執行的次數及時間及上線文關系;

2.before(asyncId)

before函數一般在 asyncId 對應的異步資源操作完成后準備執行回調前被調用,before回調函數可能被執行多次,由其被回調的次數來決定,使用時這里需要注意。

3.after(asyncId)

after回調函數一般在異步資源執行完回調函數后會立即被調用,如果在執行回調函數的過程中發生未捕獲的異常,after 事件會在觸發 “uncaughtException” 事件后被調用。

4.destroy(asyncId)

當asyncId對應的異步資源被銷毀時調用,有些異步資源的銷毀要依賴垃圾回收機制,所以有些情況下由于內存泄漏的原因,destory事件可能永遠不會被觸發。

5.promiseResolve(asyncId)

當 Promise 構造器中的 resovle 函數被執行時,promiseResolve 事件被觸發。有些情況下,有些 resolve 函數是被隱式執行的,比如 .then 函數會返回一個新的 Promise,這個時候也會被調用。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const async_hooks = require('async_hooks');
 
// 獲取當前執行上下文的 asyncId
const eid = async_hooks.executionAsyncId();
 
// 獲取觸發當前函數的 asyncId
const tid = async_hooks.triggerAsyncId();
 
// 創建新的AsyncHook實例。所有這些回調都是可選的
const asyncHook =
    async_hooks.createHook({ init, before, after, destroy, promiseResolve });
 
// 需要顯示聲明 才能執行
asyncHook.enable();
 
// 禁止監聽新的異步事件。
asyncHook.disable();
 
function init(asyncId, type, triggerAsyncId, resource) { }
 
function before(asyncId) { }
 
function after(asyncId) { }
 
function destroy(asyncId) { }
 
function promiseResolve(asyncId) { }

Promise

promise是比較特殊的一種情況,如果足夠細心init方法中的type中你就會發現其中并沒有PROMISE。如果僅使用ah.executionAsyncId()來獲取Promise的的asyncId的話,是不能取得正確的ID的,只有在添加了實際的hook只后,async_hooks才會給Promise的回調創建asyncId。

換句話說,由于V8對于獲取 asyncId 的執行成本比較高,所以默認情況下,我們是不給 Promise 分配新的 asyncId。
也就是說默認情況下,我們使用promises或者 async/await 時是獲取不到當前上下文正確的asyncId和triggerId。不過沒關系,我們可以通過執行async_hooks.createHook(callbacks).enable()函數強制開啟對Promise分配asyncId。

?
1
2
3
4
5
6
const async_hooks = require('async_hooks');
 
const asyncHook = async_hooks.createHook({
  init(asyncId, type, triggerAsyncId, resource) { },
  destroy(asyncId) { }
})
?
1
2
3
4
5
asyncHook.enable();
 
Promise.resolve(123).then(() => {
  console.log(`asyncId ${async_hooks.executionAsyncId()} triggerId ${async_hooks.triggerAsyncId()}`);
});

另外Promise只會觸發init和promiseResolve鉤子事件函數,而before和after事件的鉤子函數只會在Promise的鏈式調用時被觸發,也就是說只有在.then/.catch函數中生成的Promise時才會被觸發。

?
1
2
3
4
5
new Promise(resolve => {
    resolve(123);
}).then(data => {
    console.log(data);
})

可以發現,上面的存在兩個Promise,第一個是new實例化創建的,第二個是then創建的(不明白的可以查看之前的Promise源碼文章)。

這里的順序是執行new Promise的時候會調用自身的init函數,然后在執行resolve的時候調用promiseResolve函數。接著在then方法中執行第二個Promise的init函數,然后執行第二個Promise的before,promiseResovle,after函數。

異常處理

如果注冊的async-hook回調函數中發生異常,那么服務將打印錯誤日志并立即退出,同時所有de 監聽器將被移除,同時會觸發 ‘exit' 事件退出程序。

之所以會立即退出進程,是因為如果這些async-hook 函數運行不穩定,下一個相同事件被觸發時很可能又拋出異常,這些函數主要就是為了監聽異步事件的,如果不穩定應該及時發現并進行更正。

在異步鉤子回調中打印日志

由于 console.log 函數也是一個異步調用,如果我們在 async-hook 函數中再調用 console.log 那么將再次觸發相應的 hook 事件,造成死循環調用,所以我們在 async-hook 函數中必須使用同步打印日志方式來跟蹤,可以使用 fs.writeSync 函數:

?
1
2
3
4
5
6
const fs = require('fs');
const util = require('util');
 
function debug(...args) {
  fs.writeFileSync('log.out', `${util.format(...args)}\n`, { flag: 'a' });
}

[參考文獻-AsyncHooks] (https://nodejs.org/dist/latest-v15.x/docs/api/async_hooks.html)

到此這篇關于Node8中AsyncHooks異步生命周期的文章就介紹到這了,更多相關Node AsyncHooks異步生命周期內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://juejin.cn/post/6950545906181767205

延伸 · 閱讀

精彩推薦
  • node.js詳解node.js創建一個web服務器(Server)的詳細步驟

    詳解node.js創建一個web服務器(Server)的詳細步驟

    這篇文章主要介紹了詳解node.js創建一個web服務器(Server)的詳細步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,...

    王佳斌8952021-12-31
  • node.jsNode.js 中如何收集和解析命令行參數

    Node.js 中如何收集和解析命令行參數

    這篇文章主要介紹了Node.js 中如何收集和解析命令行參數,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋...

    descire8802021-12-28
  • node.js在瀏覽器中,把 Vite 跑起來了!

    在瀏覽器中,把 Vite 跑起來了!

    大家好,我是 ssh,前幾天在推上沖浪的時候,看到 Francois Valdy 宣布他制作了 browser-vite[1],成功把 Vite 成功在瀏覽器中運行起來了。這引起了我的興趣,如...

    前端從進階到入院9282022-01-11
  • node.jsk8s node節點重新加入master集群的實現

    k8s node節點重新加入master集群的實現

    這篇文章主要介紹了k8s node節點重新加入master集群的實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋...

    Scarborought13922022-01-22
  • node.jsnodejs中使用worker_threads來創建新的線程的方法

    nodejs中使用worker_threads來創建新的線程的方法

    這篇文章主要介紹了nodejs中使用worker_threads來創建新的線程的方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友...

    flydean程序那些事8982022-01-06
  • node.jslinux服務器快速卸載安裝node環境(簡單上手)

    linux服務器快速卸載安裝node環境(簡單上手)

    這篇文章主要介紹了linux服務器快速卸載安裝node環境(簡單上手),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需...

    mose-x8462022-01-22
  • node.jsrequire加載器實現原理的深入理解

    require加載器實現原理的深入理解

    這篇文章主要給大家介紹了關于require加載器實現原理的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需...

    隱冬8462022-03-03
  • node.jsNode.js ObjectWrap 的弱引用問題

    Node.js ObjectWrap 的弱引用問題

    最近在寫 Node.js Addon 的過程中,遇到了一個問題,然后發現是 ObjectWrap 弱引用導致的,本文介紹一下具體的問題和排查過程,以及 ObjectWrap 的使用問題。...

    編程雜技9852022-01-04
主站蜘蛛池模板: 91原创国产 | 四虎影院最新网址 | 日本午夜大片免费观看视频 | 日韩一区二区三区不卡视频 | 欧美艳星kagneyiynn高清 | 亚洲第一区欧美日韩精品 | 草草影院免费 | 99久久伊人精品波多野结衣 | 精彩国产萝视频在线 | 无限好资源免费观看 | 男人疯狂进女人下部视频动漫 | 无人区尖叫之夜美女姐姐视频 | 成人国产精品视频频 | 91精品啪在线观看国产线免费 | tobu8中国在线观看免费视频 | 性欧美video 性满足久久久久久久久 | 国内精品一区二区在线观看 | 强行扒开美女大腿挺进 | 从后面撕开老师的丝袜动态图 | 久久国产精品人妻中文 | 精品久久久久久久高清 | 女人张开腿让男人桶爽 | 吻戏辣妞范1000免费体验 | 青草久久伊人 | 人禽l交视频在线播放 视频 | 免费观看美景之屋 | 日本理论片中文在线观看2828 | 精品日韩欧美一区二区三区在线播放 | 亚洲aⅴ男人的天堂在线观看 | 91入口免费网站大全 | 疯狂伦交1一6 小说 风间由美在线 | 成人永久免费福利视频网站 | 无遮挡免费h肉动漫在线观看 | 亚洲国产欧美目韩成人综合 | 午夜宅男网 | 小柔的性放荡羞辱日记 | 成人免费视频在 | 国产91素人搭讪系列天堂 | 96免费精品视频在线 | 香蕉免费看一区二区三区 | 北条麻妃黑人正在播放 |