美文网首页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模块

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