美文网首页Node.js我爱编程
[Node.js] async_hooks模块

[Node.js] async_hooks模块

作者: 何幻 | 来源:发表于2017-07-20 17:45 被阅读1884次

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?

相关文章

  • [Node.js] async_hooks模块

    1. 异步资源 node 8.2中的async_hooks模块,提供了一组API用来跟踪应用中的异步资源(asyn...

  • 关于node.js一些模块的记录「FS模块」

    目录 Node.JS教程 FS模块 Path模块 FS模块 Path模块 Node.js path 模块提供了一些...

  • 5-创建及发布node.js模块

    创建node.js模块node.js模块是可以发布在npm上的package,当你要创建一个node.js模块时,...

  • Node.js HTTP模块

    内置HTTP模块 Node.js 中有 HTTP 这个内置模块,HTTP 模块允许 Node.js 通过超文本传输...

  • Node.js模块

    Node.js 模块和 Node.js 包介绍。 一、Node.js模块 每一个Node.js都是一个Node.j...

  • Node.js与内置模块

    目录: 认识Node.js fs文件系统模块 path路径模块 http模块 认识Node.js 为啥JavaSc...

  • Node学习(6)--模块系统

    Node.js模块系统 为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统。模块是No...

  • node.js - 基础之module

    1. node.js模块概述 为了让node.js的文件可以相互调用,node.js提供了一个简单的模块系统。模块...

  • 6-1 Node.js 模块

    Node.js 的模块 为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统。模块是No...

  • node.js(十一)

    Node.js模块系统为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统。模块是Nod...

网友评论

    本文标题:[Node.js] async_hooks模块

    本文链接:https://www.haomeiwen.com/subject/jlfjkxtx.html