async_hooks: Awaited thenables have no async context after first tick
kjin opened this issue · comments
- Version: 11.9, 10.15
- Platform: macOS
- Subsystem: async_hooks
If I do await { then: fn }
, then code running in fn
will always see an async ID of 1
until after an Immediate (or "lower priority" tick) is made, then it will start seeing 0
instead. I expect it instead to be the same async ID as the resource in which the call was made... see the below repro for expectations vs. actual results.
The culprit here is probably the same as in #22360, but discussion there has stalled. Also, I think the example here is easier to comprehend.
Repro
const asyncHooks = require('async_hooks');
// activate async_hooks
asyncHooks.createHook({
init: () => {}
}).enable();
// a custom thenable
const thenable = () => ({
then: (fn) => {
console.log(asyncHooks.executionAsyncId());
fn();
}
});
async function main() {
// On first tick
console.log(asyncHooks.executionAsyncId()); // prints 1
await thenable(); // prints 1 (expected)
// After first thenable
console.log(asyncHooks.executionAsyncId()); // prints incremented ID
await thenable(); // still prints 1 (unexpected)
// After high-priority tick
await new Promise(res => process.nextTick(res)); // tick nextTick queue
console.log(asyncHooks.executionAsyncId()); // prints incremented ID
await thenable(); // still prints 1 (unexpected)
// After Immediate
await new Promise(res => setImmediate(res)); // tick immediate
console.log(asyncHooks.executionAsyncId()); // prints incremented ID
await thenable(); // prints 0 (unexpected)
// A workaround
console.log(asyncHooks.executionAsyncId()); // prints incremented ID
await thenable().then(_ => _); // prints same as above (expected)
}
main().catch(console.error);
Real-world use case example
The knex
module seems to sometimes make DB queries in a Promise.prototype.then
wrapper. If the Promise
implementation happens to be userland, like in bluebird
, then async context will be unknown at the DB query call site.
if you want to track bluebird promises, bluebird needs to be async_hooks aware, or you need to patch async_hooks behaviour into it. async_hooks can't track what it doesn't know.
@devsnek I'm aware that bluebird doesn't currently have async_hooks interop, I believe it's an orthogonal issue. The bug here isn't whether the correct async ID is present in code running in the then
callback, it's whether the correct async ID is present in the implementation of then
itself.