On this page

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();
M

promiseHooks.onInit

History
promiseHooks.onInit(init): Function
Attributes
The init callback to call when a promise is created.
Returns:Function
Call to stop the hook.

The 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) => {});
M

promiseHooks.onSettled

History
promiseHooks.onSettled(settled): Function
Attributes
settled:Function
The settled callback to call when a promise is resolved or rejected.
Returns:Function
Call to stop the hook.

The 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) => {});
M

promiseHooks.onBefore

History
promiseHooks.onBefore(before): Function
Attributes
before:Function
The before callback to call before a promise continuation executes.
Returns:Function
Call to stop the hook.

The 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) => {});
M

promiseHooks.onAfter

History
promiseHooks.onAfter(after): Function
Attributes
after:Function
The after callback to call after a promise continuation executes.
Returns:Function
Call to stop the hook.

The 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) => {});
M

promiseHooks.createHook

History
promiseHooks.createHook(callbacks): Function
Attributes
Returns:Function
Used for disabling hooks

The 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(promise, parent): void
Attributes
promise:Promise
The promise being created.
parent:Promise
The promise continued from, if applicable.

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
Attributes
promise:Promise

Called 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
Attributes
promise:Promise

Called 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
Attributes
promise:Promise

Called when the promise receives a resolution or rejection value. This may occur synchronously in the case of Promise.resolve() or Promise.reject().