Permalink
Browse files

feat(async_hook): add async resource events

  • Loading branch information...
suguru03 committed Dec 14, 2017
1 parent a974e1b commit 87c0c998df58c20e1812f86828c7d4df663f5887
Showing with 95 additions and 9 deletions.
  1. +21 −5 lib/aigle.js
  2. +5 −2 lib/internal/async.js
  3. +23 −2 lib/internal/util.js
  4. +46 −0 test/lib/test.asyncHooks.js
View
@@ -3,7 +3,10 @@
const { AigleCore, AigleProxy } = require('aigle-core');
const Queue = require('./internal/queue');
const invokeAsync = require('./internal/async');
const {
invoke: invokeAsync,
AsyncResource
} = require('./internal/async');
const {
VERSION,
INTERNAL,
@@ -16,6 +19,7 @@ const {
callReceiver
} = require('./internal/util');
let stackTraces = false;
let asyncResource = false;
class Aigle extends AigleCore {
@@ -41,6 +45,7 @@ class Aigle extends AigleCore {
constructor(executor) {
super();
this._resolved = 0;
this._resource = asyncResource && new AsyncResource('PROMISE');
this._value = undefined;
this._key = undefined;
this._receiver = undefined;
@@ -53,6 +58,9 @@ class Aigle extends AigleCore {
this._execute(executor);
}
// TODO check performance
// _execute() {}
/**
* @param {Function} onFulfilled
* @param {Function} [onRejected]
@@ -3775,14 +3783,22 @@ function addProxy(promise, Proxy, arg1, arg2, arg3) {
* @param {Object} opts
* @param {boolean} [opts.longStackTraces]
* @param {boolean} [opts.cancellation]
* @param {boolean} [opts.asyncResource]
*/
function config(opts) {
opts = opts || {};
if (opts.longStackTraces !== undefined) {
const {
longStackTraces,
cancellation,
asyncResource: resource
} = opts || {};
if (longStackTraces !== undefined) {
stackTraces = !!opts.longStackTraces;
}
if (opts.cancellation !== undefined) {
Aigle.prototype._execute = opts.cancellation ? executeWithCancel : execute;
if (cancellation !== undefined) {
Aigle.prototype._execute = cancellation ? executeWithCancel : execute;
}
if (resource !== undefined) {
asyncResource = resource;
}
}
View
@@ -4,6 +4,11 @@ const queue = Array(8);
let len = 0;
let ticked = false;
const { supportAsyncHook } = require('./util');
const AsyncResource = supportAsyncHook && require('async_hooks').AsyncResource;
module.exports = Object.assign(exports, { invoke, AsyncResource });
function tick() {
let i = -1;
while (++i < len) {
@@ -29,5 +34,3 @@ function invoke(promise) {
}
queue[len++] = promise;
}
module.exports = invoke;
View
@@ -5,13 +5,23 @@ const { version: VERSION } = require('../../package.json');
const DEFAULT_LIMIT = 8;
const errorObj = { e: undefined };
const iteratorSymbol = typeof Symbol === 'function' && Symbol.iterator;
const node = typeof process !== 'undefined' && process.toString() === '[object process]';
const supportAsyncHook = node && (() => {
const support = '9.3.0';
const [smajor, sminor] = support.match(/\d+/g);
const version = process.versions.node;
const [vmajor, vminor] = version.match(/\d+/g);
return vmajor > smajor || vmajor === smajor && vminor >= sminor;
})();
module.exports = {
VERSION,
DEFAULT_LIMIT,
INTERNAL,
PENDING,
UNHANDLED,
node,
supportAsyncHook,
defaultIterator,
errorObj,
iteratorSymbol,
@@ -71,6 +81,13 @@ function call3(handler, arg1, arg2, arg3) {
}
}
function call1WithAsyncResource(resource, handler, value) {
resource.emitBefore();
const promise = call1(handler, value);
resource.emitAfter();
return promise;
}
function apply(handler, array) {
try {
switch (array.length) {
@@ -96,7 +113,9 @@ function callResolve(receiver, onFulfilled, value) {
receiver._resolve(value);
return;
}
const promise = call1(onFulfilled, value);
const promise = receiver._resource ?
call1WithAsyncResource(receiver._resource, onFulfilled, value) :
call1(onFulfilled, value);
if (promise === errorObj) {
receiver._reject(errorObj.e);
return;
@@ -109,7 +128,9 @@ function callReject(receiver, onRejected, reason) {
receiver._reject(reason);
return;
}
const promise = call1(onRejected, reason);
const promise = receiver._resource ?
call1WithAsyncResource(receiver._resource, onRejected, reason) :
call1(onRejected, reason);
if (promise === errorObj) {
receiver._reject(errorObj.e);
return;
@@ -0,0 +1,46 @@
'use strict';
const assert = require('assert');
const _ = require('lodash');
const Aigle = require('../../');
const { supportAsyncHook } = require('../../lib/internal/util');
const describe = supportAsyncHook ? global.describe : global.describe.skip;
describe('asyncHook', () => {
const { createHook, executionAsyncId, AsyncResource } = require('async_hooks');
const hooks = {};
const create = name => hooks[name] = (asyncId, ...args) => map[name].set(asyncId, args);
_.forEach(['init', 'before', 'after', 'destroy', 'promiseResolve'], create);
const map = _.mapValues(hooks, () => new Map());
const hook = createHook(hooks);
before(() => Aigle.config({ asyncResource: true }));
after(() => Aigle.config({ asyncResource: false }));
afterEach(() => {
hook.disable();
_.forOwn(map, map => map.clear());
});
it('should create an async resource', () => {
hook.enable();
const tid = executionAsyncId();
return Aigle.resolve()
.then(() => {
const id = executionAsyncId();
const args = map.init.get(id);
assert.ok(args);
const [type, triggerId, resource] = args;
assert.strictEqual(type, 'PROMISE');
assert.strictEqual(triggerId, tid);
assert.ok(resource instanceof AsyncResource);
});
});
});

0 comments on commit 87c0c99

Please sign in to comment.