[Node.js] async_hooks模塊

1. 異步資源

node 8.2中的async_hooks模塊,提供了一組API用來跟蹤應(yīng)用中的異步資源(asynchronous resources)。

與異步資源相關(guān)的回調(diào)函數(shù)(callback),
可能會被調(diào)用多次,例如net.createServerconnection事件,
也可能會被調(diào)用一次,例如fs.open。

異步資源也可能在調(diào)用回調(diào)函數(shù)之前就已經(jīng)被關(guān)閉了。

2. async_hooks用法

const asyncHook = require('async_hooks');

const hook = asyncHooks.createHook({
    init(asyncId, type, triggerAsyncId, resource) {
    },
    before(asyncId) {
    },
    after(asyncId) {
    },
    destroy(asyncId) {
    }
});

hook.enable();

以上代碼創(chuàng)建一個(gè)hook,它可以用來跟蹤應(yīng)用中所有的異步資源,
當(dāng)資源在被初始化,回調(diào)之前,回調(diào)之后,銷毀后,將自動(dòng)觸發(fā)init,beforeafter,destroy。

我們可以使用hook.enable();啟用跟蹤,還可以使用hook.disable();來關(guān)閉。
其中asyncId,triggerAsyncId的介紹見下文。

3. 當(dāng)心console.log會造成無限循環(huán)

我們通常使用的console.log,向控制臺打印消息,
然而,它卻是一個(gè)異步操作(asynchronous operation),
所以,async_hooks也可以用來跟蹤它。
(參考:Printing in AsyncHooks callbacks

因此,如果在上述init,before,after,destroy事件處理函數(shù)中出現(xiàn)了console.log,就會導(dǎo)致無限循環(huán)。

我們可以使用fs.writeSync(1, msg)來代替console.log,
其中writeSync函數(shù)的第一個(gè)參數(shù)為文件描述符(file descriptor),
1表示標(biāo)準(zhǔn)輸出(standard output)。

4. 完整的例子

const fs = require('fs');
const asyncHooks = require('async_hooks');

const hook = asyncHooks.createHook({
    init(asyncId, type, triggerAsyncId, resource) {
        fs.writeSync(1, `init: asyncId-${asyncId},type-${type},triggerAsyncId-${triggerAsyncId}\n`);
    },
    before(asyncId) {
        fs.writeSync(1, `before: asyncId-${asyncId}\n`);
    },
    after(asyncId) {
        fs.writeSync(1, `after: asyncId-${asyncId}\n`);
    },
    destroy(asyncId) {
        fs.writeSync(1, `destroy: asyncId-${asyncId}\n`);
    }
});

hook.enable();
console.log('hello');

// hook.disable();    // 注意,這里不要disable,否則只能觸發(fā)init事件

執(zhí)行后,輸出:

init: asyncId-2, type-TTYWRAP, triggerAsyncId-1
init: asyncId-3, type-SIGNALWRAP, triggerAsyncId-1
init: asyncId-4, type-TTYWRAP, triggerAsyncId-1
hello
init: asyncId-5, type-TickObject, triggerAsyncId-1
before: asyncId-5
after: asyncId-5
destroy: asyncId-5

5. 自定義AsyncResource

async_hooks模塊除了可以跟蹤node中內(nèi)置的異步資源,還可以跟蹤自定義的資源,
要做到這一點(diǎn),我們需要繼承AsyncResource類,然后手動(dòng)觸發(fā)事件。

其中,AsyncResource類,是async_hooks模塊導(dǎo)出對象的一個(gè)屬性asyncHooks.AsyncResource。

const fs = require('fs');
const asyncHooks = require('async_hooks');

class MyResource extends asyncHooks.AsyncResource {
    constructor() {
        super('my-resource');
    }

    asyncMethod(callback) {
        this.emitBefore();
        callback();
        this.emitAfter();
    }

    close() {
        this.emitDestroy();
    }
}

const hook = asyncHooks.createHook({
    init(asyncId, type, triggerAsyncId, resource) {
        fs.writeSync(1, `init: asyncId-${asyncId}, type-${type}, triggerAsyncId-${triggerAsyncId}\n`);
    },
    before(asyncId) {
        fs.writeSync(1, `before: asyncId-${asyncId}\n`);
    },
    after(asyncId) {
        fs.writeSync(1, `after: asyncId-${asyncId}\n`);
    },
    destroy(asyncId) {
        fs.writeSync(1, `destroy: asyncId-${asyncId}\n`);
    }
});

hook.enable();

let resource = new MyResource;
resource.asyncMethod(() => { });
resource.close();

// hook.disable();    // 注意,這里不要disable,否則將不會觸發(fā)destroy事件

注:
emitDestroy不是同步調(diào)用的,
所以emitDestroy之后,馬上將hook.disable();,destroy事件就不觸發(fā)了。

6. async scope和async id

為了對異步資源實(shí)現(xiàn)跟蹤,
node對每一個(gè)函數(shù)(不論異步還是同步)提供了一個(gè) async scope,
我們可以通過調(diào)用asyncHooks.executionAsyncId();來獲取當(dāng)前函數(shù)的asyncId
通過調(diào)用asyncHooks.triggerAsyncId();來獲取當(dāng)前函數(shù)調(diào)用者的asyncId。

const asyncHooks = require('async_hooks');

console.log(`top level: ${asyncHooks.executionAsyncId()}`);

const f = () => {
    console.log(`f: ${asyncHooks.executionAsyncId()}`);
};

f();

const g = () => {
    console.log(`setTimeout: ${asyncHooks.executionAsyncId()}`);
}

setTimeout(g, 0);
setTimeout(g, 0);

最終輸出結(jié)果:

top level: 1
f: 1
setTimeout: 6
setTimeout: 8

注:
(1)top-level的asyncId總是1。
(2)調(diào)用同步函數(shù),不會改變其調(diào)用者的asyncId,例如,函數(shù)f內(nèi)的asyncId和其調(diào)用者(即top-level)的asyncId相同。
(3)同一個(gè)函數(shù),被不同時(shí)刻進(jìn)行異步調(diào)用,會分配不同的asyncId,例如,函數(shù)g中的asyncId。


參考

Node.js v8.2.0 Documentation: Async Hooks
Node.js v8.x 新特性 Async Hook 簡介
What does fs.writeSync(1, “a string”) mean in Node.js?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • topics: 1.The Node.js philosophy 2.The reactor pattern 3....
    宮若石閱讀 1,238評論 0 1
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,554評論 19 139
  • 異步編程對JavaScript語言太重要。Javascript語言的執(zhí)行環(huán)境是“單線程”的,如果沒有異步編程,根本...
    呼呼哥閱讀 7,404評論 5 22
  • # 模塊機(jī)制 node采用模塊化結(jié)構(gòu),按照CommonJS規(guī)范定義和使用模塊,模塊與文件是一一對應(yīng)關(guān)系,即加載一個(gè)...
    RichRand閱讀 2,736評論 0 3
  • “連狗都不如”,是對藏獒處境的最好概括。流浪藏獒對青海造成的破壞是全方位的。 流浪藏獒不僅會捕食珍稀野生動(dòng)物,還有...
    羅掌柜real閱讀 532評論 0 0

友情鏈接更多精彩內(nèi)容