亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Node8中AsyncHooks異步生命周期

 更新時間:2021年04月15日 10:01:36   作者:隱冬  
這篇文章主要介紹了Node8中AsyncHooks異步生命周期,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

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

const async_hooks = require('async_hooks');

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

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

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

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

async_hooks規(guī)則

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

這在多個異步任務(wù)并行的時候,asyncId可以使我們可以很好的區(qū)分要監(jiān)聽的是哪一個異步任務(wù)。

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

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

每一個async scope中都有一個triggerAsyncId表示當(dāng)前函數(shù)是由那個async scope觸發(fā)生成的;

通過 asyncId 和 triggerAsyncId 我們可以很方便的追蹤整個異步的調(diào)用關(guān)系及鏈路。

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

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

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

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

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
});

異步函數(shù)的生命周期

當(dāng)然實際應(yīng)用中的async_hooks并不是這樣使用的,他正確的用法是在所有異步任務(wù)創(chuàng)建、執(zhí)行前、執(zhí)行后、銷毀后,觸發(fā)回調(diào),所有回調(diào)會傳入asyncId。

我們可以使用async_hooks.createHook來創(chuàng)建一個異步資源的鉤子,這個鉤子接收一個對象作為參數(shù)來注冊一些關(guān)于異步資源生命周期中可能發(fā)生事件的回調(diào)函數(shù)。每當(dāng)異步資源被創(chuàng)建/執(zhí)行/銷毀時這些鉤子函數(shù)會被觸發(fā)。

const async_hooks = require('async_hooks');

const asyncHook = async_hooks.createHook({
  init(asyncId, type, triggerAsyncId, resource) { },
  destroy(asyncId) { }
})

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

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

  • init 回調(diào)函數(shù)一般在異步資源初始化的時候被觸發(fā)。
  • asyncId: 每一個異步資源都會生成一個唯一性標志
  • type: 異步資源的類型,一般都是資源的構(gòu)造函數(shù)的名字。

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: 表示觸發(fā)當(dāng)前異步資源被創(chuàng)建的對應(yīng)的 async scope 的 asyncId
  • resource: 代表被初始化的異步資源對象

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

2.before(asyncId)

before函數(shù)一般在 asyncId 對應(yīng)的異步資源操作完成后準備執(zhí)行回調(diào)前被調(diào)用,before回調(diào)函數(shù)可能被執(zhí)行多次,由其被回調(diào)的次數(shù)來決定,使用時這里需要注意。

3.after(asyncId)

after回調(diào)函數(shù)一般在異步資源執(zhí)行完回調(diào)函數(shù)后會立即被調(diào)用,如果在執(zhí)行回調(diào)函數(shù)的過程中發(fā)生未捕獲的異常,after 事件會在觸發(fā) “uncaughtException” 事件后被調(diào)用。

4.destroy(asyncId)

當(dāng)asyncId對應(yīng)的異步資源被銷毀時調(diào)用,有些異步資源的銷毀要依賴垃圾回收機制,所以有些情況下由于內(nèi)存泄漏的原因,destory事件可能永遠不會被觸發(fā)。

5.promiseResolve(asyncId)

當(dāng) Promise 構(gòu)造器中的 resovle 函數(shù)被執(zhí)行時,promiseResolve 事件被觸發(fā)。有些情況下,有些 resolve 函數(shù)是被隱式執(zhí)行的,比如 .then 函數(shù)會返回一個新的 Promise,這個時候也會被調(diào)用。

const async_hooks = require('async_hooks');

// 獲取當(dāng)前執(zhí)行上下文的 asyncId
const eid = async_hooks.executionAsyncId();

// 獲取觸發(fā)當(dāng)前函數(shù)的 asyncId
const tid = async_hooks.triggerAsyncId();

// 創(chuàng)建新的AsyncHook實例。所有這些回調(diào)都是可選的
const asyncHook =
    async_hooks.createHook({ init, before, after, destroy, promiseResolve });

// 需要顯示聲明 才能執(zhí)行
asyncHook.enable();

// 禁止監(jiān)聽新的異步事件。
asyncHook.disable();

function init(asyncId, type, triggerAsyncId, resource) { }

function before(asyncId) { }

function after(asyncId) { }

function destroy(asyncId) { }

function promiseResolve(asyncId) { }

Promise

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

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

const async_hooks = require('async_hooks');

const asyncHook = async_hooks.createHook({
  init(asyncId, type, triggerAsyncId, resource) { },
  destroy(asyncId) { }
})
asyncHook.enable();

Promise.resolve(123).then(() => {
  console.log(`asyncId ${async_hooks.executionAsyncId()} triggerId ${async_hooks.triggerAsyncId()}`);
});

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

new Promise(resolve => {
    resolve(123);
}).then(data => {
    console.log(data);
})

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

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

異常處理

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

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

在異步鉤子回調(diào)中打印日志

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

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)

到此這篇關(guān)于Node8中AsyncHooks異步生命周期的文章就介紹到這了,更多相關(guān)Node AsyncHooks異步生命周期內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Node.js高級編程cluster環(huán)境及源碼調(diào)試詳解

    Node.js高級編程cluster環(huán)境及源碼調(diào)試詳解

    這篇文章主要為大家介紹了Node.js高級編程cluster環(huán)境及源碼調(diào)試詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12
  • Node快速切換版本、版本回退(降級)、版本更新(升級)

    Node快速切換版本、版本回退(降級)、版本更新(升級)

    這篇文章主要介紹了Node快速切換版本、版本回退(降級)、版本更新(升級),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • 手把手教你如何使用nodejs編寫cli命令行

    手把手教你如何使用nodejs編寫cli命令行

    這篇文章主要介紹了手把手教你如何使用nodejs編寫cli命令行,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-11-11
  • Node.js實現(xiàn)文件上傳的示例

    Node.js實現(xiàn)文件上傳的示例

    本篇文章主要介紹了Node.js實現(xiàn)文件上傳的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-06-06
  • nodejs body-parser 解析post數(shù)據(jù)實例

    nodejs body-parser 解析post數(shù)據(jù)實例

    下面小編就為大家?guī)硪黄猲odejs body-parser 解析post數(shù)據(jù)實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07
  • linux 下以二進制的方式安裝 nodejs

    linux 下以二進制的方式安裝 nodejs

    這篇文章主要介紹了linux 下以二進制的方式安裝 nodejs,文中給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-02-02
  • Node中文件斷點續(xù)傳原理和方法總結(jié)

    Node中文件斷點續(xù)傳原理和方法總結(jié)

    在之前做過一個小項目,涉及到了文件上傳,在大文件上面使用了斷點續(xù)傳,降低了服務(wù)器方面的壓力,現(xiàn)在小編把Node中文件斷點續(xù)傳原理和方法總結(jié)分享給大家,感興趣的朋友一起看看吧
    2022-01-01
  • Node.js 異步異常的處理與domain模塊解析

    Node.js 異步異常的處理與domain模塊解析

    本篇文章主要介紹了Node.js 異步異常的處理與domain模塊解析,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • nodejs插件及用法整理

    nodejs插件及用法整理

    在本篇文章里小編給大家整理的是一篇關(guān)于nodejs插件及用法相關(guān)內(nèi)容,有興趣的朋友們可以跟著學(xué)習(xí)參考下。
    2021-11-11
  • node.js中的events.EventEmitter.listenerCount方法使用說明

    node.js中的events.EventEmitter.listenerCount方法使用說明

    這篇文章主要介紹了node.js中的events.EventEmitter.listenerCount方法使用說明,本文介紹了events.EventEmitter.listenerCount的方法說明、語法、使用實例和實現(xiàn)源碼,需要的朋友可以參考下
    2014-12-12

最新評論