1. 異步資源
node 8.2中的async_hooks
模塊浓恶,提供了一組API用來跟蹤應(yīng)用中的異步資源(asynchronous resources)院崇。
與異步資源相關(guān)的回調(diào)函數(shù)(callback)嘁信,
可能會(huì)被調(diào)用多次尤蛮,例如net.createServer
的connection
事件呐馆,
也可能會(huì)被調(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
端朵,before
好芭,after
,destroy
冲呢。
我們可以使用hook.enable();
啟用跟蹤舍败,還可以使用hook.disable();
來關(guān)閉。
其中asyncId
敬拓,triggerAsyncId
的介紹見下文邻薯。
3. 當(dāng)心console.log會(huì)造成無限循環(huán)
我們通常使用的console.log
,向控制臺(tái)打印消息乘凸,
然而厕诡,它卻是一個(gè)異步操作(asynchronous operation),
所以翰意,async_hooks
也可以用來跟蹤它木人。
(參考:Printing in AsyncHooks callbacks)
因此信柿,如果在上述init
冀偶,before
,after
渔嚷,destroy
事件處理函數(shù)中出現(xiàn)了console.log
进鸠,就會(huì)導(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)出對(duì)象的一個(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,否則將不會(huì)觸發(fā)destroy事件
注:
emitDestroy
不是同步調(diào)用的比藻,
所以emitDestroy
之后铝量,馬上將hook.disable();
,destroy
事件就不觸發(fā)了银亲。
6. async scope和async id
為了對(duì)異步資源實(shí)現(xiàn)跟蹤款违,
node對(duì)每一個(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ù)请梢,不會(huì)改變其調(diào)用者的asyncId
赠尾,例如,函數(shù)f
內(nèi)的asyncId
和其調(diào)用者(即top-level)的asyncId
相同毅弧。
(3)同一個(gè)函數(shù)气嫁,被不同時(shí)刻進(jìn)行異步調(diào)用,會(huì)分配不同的asyncId
够坐,例如寸宵,函數(shù)g
中的asyncId
。
參考
Node.js v8.2.0 Documentation: Async Hooks
Node.js v8.x 新特性 Async Hook 簡(jiǎn)介
What does fs.writeSync(1, “a string”) mean in Node.js?