async_hooks模块

1. 异步资源

node 8.2中的async_hooks模块,提供了一组API用来跟踪应用中的异步资源(asynchronous resources)。

与异步资源相关的回调函数(callback),
可能会被调用多次,例如net.createServerconnection事件,
也可能会被调用一次,例如fs.open

异步资源也可能在调用回调函数之前就已经被关闭了。

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

以上代码创建一个hook,它可以用来跟踪应用中所有的异步资源,
当资源在被初始化,回调之前,回调之后,销毁后,将自动触发initbeforeafterdestroy

我们可以使用hook.enable();启用跟踪,还可以使用hook.disable();来关闭。
其中asyncIdtriggerAsyncId的介绍见下文。

3. 当心console.log会造成无限循环

我们通常使用的console.log,向控制台打印消息,
然而,它却是一个异步操作(asynchronous operation),
所以,async_hooks也可以用来跟踪它。
(参考:Printing in AsyncHooks callbacks

因此,如果在上述initbeforeafterdestroy事件处理函数中出现了console.log,就会导致无限循环。

我们可以使用fs.writeSync(1, msg)来代替console.log
其中writeSync函数的第一个参数为文件描述符(file descriptor),
1表示标准输出(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,否则只能触发init事件 

执行后,输出:

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中内置的异步资源,还可以跟踪自定义的资源,
要做到这一点,我们需要继承AsyncResource类,然后手动触发事件。

其中,AsyncResource类,是async_hooks模块导出对象的一个属性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,否则将不会触发destroy事件 

注:
emitDestroy不是同步调用的,
所以emitDestroy之后,马上将hook.disable();destroy事件就不触发了。

6. async scope和async id

为了对异步资源实现跟踪,
node对每一个函数(不论异步还是同步)提供了一个 async scope,
我们可以通过调用asyncHooks.executionAsyncId();来获取当前函数的asyncId
通过调用asyncHooks.triggerAsyncId();来获取当前函数调用者的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); 

最终输出结果:

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

注:
(1)top-level的asyncId总是1
(2)调用同步函数,不会改变其调用者的asyncId,例如,函数f内的asyncId和其调用者(即top-level)的asyncId相同。
(3)同一个函数,被不同时刻进行异步调用,会分配不同的asyncId,例如,函数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?



作者:何幻
链接:https://www.jianshu.com/p/4a568dac41ed
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值