Promise hooks
History
The promiseHooks interface can be used to track promise lifecycle events.
To track all async activity, see async_hooks which internally uses this
module to produce promise lifecycle events in addition to events for other
async resources. For request context management, see AsyncLocalStorage.
import { promiseHooks } from 'node:v8'; // There are four lifecycle events produced by promises: // The `init` event represents the creation of a promise. This could be a // direct creation such as with `new Promise(...)` or a continuation such // as `then()` or `catch()`. It also happens whenever an async function is // called or does an `await`. If a continuation promise is created, the // `parent` will be the promise it is a continuation from. function init(promise, parent) { console.log('a promise was created', { promise, parent }); } // The `settled` event happens when a promise receives a resolution or // rejection value. This may happen synchronously such as when using // `Promise.resolve()` on non-promise input. function settled(promise) { console.log('a promise resolved or rejected', { promise }); } // The `before` event runs immediately before a `then()` or `catch()` handler // runs or an `await` resumes execution. function before(promise) { console.log('a promise is about to call a then handler', { promise }); } // The `after` event runs immediately after a `then()` handler runs or when // an `await` begins after resuming from another. function after(promise) { console.log('a promise is done calling a then handler', { promise }); } // Lifecycle hooks may be started and stopped individually const stopWatchingInits = promiseHooks.onInit(init); const stopWatchingSettleds = promiseHooks.onSettled(settled); const stopWatchingBefores = promiseHooks.onBefore(before); const stopWatchingAfters = promiseHooks.onAfter(after); // Or they may be started and stopped in groups const stopHookSet = promiseHooks.createHook({ init, settled, before, after, }); // Trigger the hooks by using promises const promiseLog = (word) => Promise.resolve(word).then(console.log); promiseLog('Hello'); promiseLog('World'); // To stop a hook, call the function returned at its creation. stopWatchingInits(); stopWatchingSettleds(); stopWatchingBefores(); stopWatchingAfters(); stopHookSet();
const { promiseHooks } = require('node:v8'); // There are four lifecycle events produced by promises: // The `init` event represents the creation of a promise. This could be a // direct creation such as with `new Promise(...)` or a continuation such // as `then()` or `catch()`. It also happens whenever an async function is // called or does an `await`. If a continuation promise is created, the // `parent` will be the promise it is a continuation from. function init(promise, parent) { console.log('a promise was created', { promise, parent }); } // The `settled` event happens when a promise receives a resolution or // rejection value. This may happen synchronously such as when using // `Promise.resolve()` on non-promise input. function settled(promise) { console.log('a promise resolved or rejected', { promise }); } // The `before` event runs immediately before a `then()` or `catch()` handler // runs or an `await` resumes execution. function before(promise) { console.log('a promise is about to call a then handler', { promise }); } // The `after` event runs immediately after a `then()` handler runs or when // an `await` begins after resuming from another. function after(promise) { console.log('a promise is done calling a then handler', { promise }); } // Lifecycle hooks may be started and stopped individually const stopWatchingInits = promiseHooks.onInit(init); const stopWatchingSettleds = promiseHooks.onSettled(settled); const stopWatchingBefores = promiseHooks.onBefore(before); const stopWatchingAfters = promiseHooks.onAfter(after); // Or they may be started and stopped in groups const stopHookSet = promiseHooks.createHook({ init, settled, before, after, }); // Trigger the hooks by using promises const promisePrint = (word) => Promise.resolve(word).then(console.log); promisePrint('Hello'); promisePrint('World'); // To stop a hook, call the function returned at its creation. stopWatchingInits(); stopWatchingSettleds(); stopWatchingBefores(); stopWatchingAfters(); stopHookSet();
promiseHooks.onInit(init): Function
Functioninit callback to call when a promise is created.FunctionThe init hook must be a plain function. Providing an async function will
throw as it would produce an infinite microtask loop.
import { promiseHooks } from 'node:v8'; const stop = promiseHooks.onInit((promise, parent) => {});
const { promiseHooks } = require('node:v8'); const stop = promiseHooks.onInit((promise, parent) => {});
promiseHooks.onSettled(settled): Function
Functionsettled callback to call when a promise
is resolved or rejected.FunctionThe settled hook must be a plain function. Providing an async function will
throw as it would produce an infinite microtask loop.
import { promiseHooks } from 'node:v8'; const stop = promiseHooks.onSettled((promise) => {});
const { promiseHooks } = require('node:v8'); const stop = promiseHooks.onSettled((promise) => {});
promiseHooks.onBefore(before): Function
Functionbefore callback to call before a promise
continuation executes.FunctionThe before hook must be a plain function. Providing an async function will
throw as it would produce an infinite microtask loop.
import { promiseHooks } from 'node:v8'; const stop = promiseHooks.onBefore((promise) => {});
const { promiseHooks } = require('node:v8'); const stop = promiseHooks.onBefore((promise) => {});
promiseHooks.onAfter(after): Function
Functionafter callback to call after a promise
continuation executes.FunctionThe after hook must be a plain function. Providing an async function will
throw as it would produce an infinite microtask loop.
import { promiseHooks } from 'node:v8'; const stop = promiseHooks.onAfter((promise) => {});
const { promiseHooks } = require('node:v8'); const stop = promiseHooks.onAfter((promise) => {});
promiseHooks.createHook(callbacks): Function
ObjectFunctioninit callback.Functionbefore callback.Functionafter callback.Functionsettled callback.FunctionThe hook callbacks must be plain functions. Providing async functions will throw as it would produce an infinite microtask loop.
Registers functions to be called for different lifetime events of each promise.
The callbacks init()/before()/after()/settled() are called for the
respective events during a promise's lifetime.
All callbacks are optional. For example, if only promise creation needs to
be tracked, then only the init callback needs to be passed. The
specifics of all functions that can be passed to callbacks is in the
Hook Callbacks section.
import { promiseHooks } from 'node:v8'; const stopAll = promiseHooks.createHook({ init(promise, parent) {}, });
const { promiseHooks } = require('node:v8'); const stopAll = promiseHooks.createHook({ init(promise, parent) {}, });
Key events in the lifetime of a promise have been categorized into four areas: creation of a promise, before/after a continuation handler is called or around an await, and when the promise resolves or rejects.
While these hooks are similar to those of async_hooks they lack a
destroy hook. Other types of async resources typically represent sockets or
file descriptors which have a distinct "closed" state to express the destroy
lifecycle event while promises remain usable for as long as code can still
reach them. Garbage collection tracking is used to make promises fit into the
async_hooks event model, however this tracking is very expensive and they may
not necessarily ever even be garbage collected.
Because promises are asynchronous resources whose lifecycle is tracked
via the promise hooks mechanism, the init(), before(), after(), and
settled() callbacks must not be async functions as they create more
promises which would produce an infinite loop.
While this API is used to feed promise events into async_hooks, the
ordering between the two is undefined. Both APIs are multi-tenant
and therefore could produce events in any order relative to each other.
init
init(promise, parent): void
Called when a promise is constructed. This does not mean that corresponding
before/after events will occur, only that the possibility exists. This will
happen if a promise is created without ever getting a continuation.
before(promise): void
PromiseCalled before a promise continuation executes. This can be in the form of
then(), catch(), or finally() handlers or an await resuming.
The before callback will be called 0 to N times. The before callback
will typically be called 0 times if no continuation was ever made for the
promise. The before callback may be called many times in the case where
many continuations have been made from the same promise.
after(promise): void
PromiseCalled immediately after a promise continuation executes. This may be after a
then(), catch(), or finally() handler or before an await after another
await.
settled(promise): void
PromiseCalled when the promise receives a resolution or rejection value. This may
occur synchronously in the case of Promise.resolve() or Promise.reject().