[Question]: Is possible to get parent promise from PromiseHookInit through async_hooks #13302

Open
JiaLiPassion opened this Issue May 30, 2017 · 5 comments

Comments

Projects
None yet
4 participants
  • Version:
    v8.0.0-nightly20170527f84666f923

  • Platform:

  • Subsystem:

Chrome V8 PromiseHook init hook's signature is PromiseHookInit(promise,parentPromise),
current async_hooks seems not support to get the parentPromise, is that possible to get the
the parentPromise from async_hooks init callback?

Thank you very much!

JiaLiPassion commented May 30, 2017 edited by AndreasMadsen

@jasnell , thank you ! I need this parent promise information to identify the promise which was newly created in Promise.then or not.

Also ping @AndreasMadsen, @matthewloring !

from the PromiseHook Sample https://docs.google.com/document/d/1rda3yKGHimKIhg5YeoAmCOtyURgsbTH_qaYR79FELlk/edit#

there is code like this.

const async_wrap = process.binding('async_wrap');
const chain = new Map();
const startTimes = new Map();
const endTimes = new Map();
let root;
 
function init(id, parentId) {
    if (parentId !== undefined) {
        chain.set(parentId, id);
    } else {
        root = id;
    }
}
 
function before(id) { startTimes.set(id, Date.now()); }
function after(id) { endTimes.set(id, Date.now()); }
 
async_wrap.setupHooks({init, before, after});
async_wrap.enable();
 
doSomeAsyncOp()
  .then(() => { .. })
  .then(() => { .. })
 
async_wrap.disable();
 
let totalTime = 0;
while(root = chain.get(root)) {
    totalTime += (endTimes.get(root) - startTimes.get(root));
}
 
console.log(totalTime);

const p = new Promise ( resolve => resolve(1) );
const p1 = p.then(val => { console.log(val == 1); return 2; });
const p2 = p.then(val => console.log(val == 1));
  

the call order will like

init( p )
init( p1, p )
init( p2, p )
 
before( p1 )
after( p1 )
 
before( p2 )
after( p2 )


This is the expected result, but current nodejs 8 nightly build, the code is

const async_hooks = require('async_hooks');

function init(id, provider, parentId, parentHandle) {
    process._rawDebug('init ', id, provider, parentId, parentHandle);
}

function before(id) { 
    process._rawDebug('before', id);
 }
function after(id) { 
    process._rawDebug('after', id);
 }
function destroy(id) {
    process._rawDebug('destroy', id);
}

async_hooks.createHook({init, before, after, destroy}).enable();
const p = new Promise ( resolve => {
    process._rawDebug('execute p');
    resolve(1);} );
const p1 = p.then(val => { process._rawDebug('p1 then', val); return 2; });
const p2 = p1.then(val => process._rawDebug('p2', val));

and the output is

init  2 PROMISE 1 {}
init  3 PROMISE 1 {}
init  4 PROMISE 1 {}
before 3
after 3
before 4
after 4

The parentId are all 1.

edit (AndreasMadsen): added syntax highlighting

mscdex added the promises label May 30, 2017

Member

AndreasMadsen commented May 30, 2017 edited

I need confirmation that I understand the question/feature request correctly.

Consider this code:

const async_hooks = require('async_hooks');

async_hooks.createHook({
  init(id, type, triggerId, resource) {
    process._rawDebug('init', {id, type, triggerId, resource});
  },
  before(id) {
    process._rawDebug('before', {id});
  },
  after(id) {
    process._rawDebug('after', {id});
  },
  destroy(id) {
    process._rawDebug('destroy', {id});
  }
}).enable();

const p2 = new Promise(function (resolve) {
    resolve(1);
});

const p3 = p2.then(function (val) {
  return 2;
});

This results in the following output:

init { id: 2,
  type: 'PROMISE',
  triggerId: 1,
  resource: Promise { <pending> } }
init { id: 3,
  type: 'PROMISE',
  triggerId: 1,
  resource: Promise { <pending> } }
before { id: 3 }
after { id: 3 }

You would like the init created by promise p3 (has id = 3) to have the trigger_id of promise p2 (has id = 2).

I think that is fair, it shouldn't be difficult implement. Honestly, the trigger id stuff is a little confusing to me, I would like to see a clear line when we set the trigger_id and when we just let it be the current_id.

JiaLiPassion commented May 30, 2017 edited

@AndreasMadsen , thank you for the reply.

You would like the init created by promise p3 (has id = 3) to have the trigger id of promise p2 (has id = 2).

Yes, that will help.

And If the implementation become like this, I have another question is

Init { id: 2,
type: 'PROMISE',
triggerId: 1,
resource: Promise { } }

Can I say that triggerId is 1 means this Promise is just a newly created Promise without any parent chain?

Member

AndreasMadsen commented May 30, 2017

Can I say that triggerId is 1 means this Promise is just a newly created Promise without any parent chain?

Not always, triggerId = 1 is just the trigger_id for the root, if you create a promise inside the callback of another asynchronous operation you will get another trigger_id.

If we implement as suggested, you can check if triggerId === async_hooks.currentId(). If that is true then the promise has no parent (there might be an edge case I haven't thought of).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment