Implementation:Puppeteer Puppeteer Disposable
| Property | Value |
|---|---|
| sources | packages/puppeteer-core/src/util/disposable.ts |
| domains | Utility, Resource Management |
| last_updated | 2026-02-12 00:00 GMT |
Overview
Description
The disposable module provides polyfills for the TC39 Explicit Resource Management proposal (Symbol.dispose, Symbol.asyncDispose, DisposableStack, AsyncDisposableStack, and SuppressedError). These polyfills ensure Puppeteer can use the using and await using syntax across all supported Node.js runtimes, even those that do not yet natively support these symbols and classes.
The module provides the following exports:
- disposeSymbol / asyncDisposeSymbol -- References to Symbol.dispose and Symbol.asyncDispose, with polyfill initialization if the symbols do not exist on the global Symbol object.
- DisposableStackPolyfill -- A synchronous disposable stack that tracks disposable resources and disposes them in LIFO (last-in, first-out) order. It provides methods to use (track a disposable), adopt (track a non-disposable with a disposal callback), defer (register a disposal callback), and move (transfer ownership of resources to a new stack).
- AsyncDisposableStackPolyfill -- The asynchronous counterpart of DisposableStackPolyfill, supporting async disposables and async disposal callbacks.
- SuppressedErrorPolyfill -- An Error subclass that wraps multiple errors thrown during disposal, preserving both the primary error and suppressed errors.
Each polyfill checks for the native implementation on globalThis before falling back to the polyfill class.
Usage
Puppeteer uses these primitives extensively to manage the lifecycle of browser connections, transports, event subscriptions, and other resources. Classes that own resources implement [Symbol.dispose]() or [Symbol.asyncDispose](), and callers use DisposableStack or AsyncDisposableStack to ensure proper cleanup.
Code Reference
Source Location
packages/puppeteer-core/src/util/disposable.ts
Signature
export const disposeSymbol: typeof Symbol.dispose;
export const asyncDisposeSymbol: typeof Symbol.asyncDispose;
export class DisposableStackPolyfill {
get disposed(): boolean;
dispose(): void;
use<T extends Disposable | null | undefined>(value: T): T;
adopt<T>(value: T, onDispose: (value: T) => void): T;
defer(onDispose: () => void): void;
move(): DisposableStackPolyfill;
[disposeSymbol](): void;
}
export class AsyncDisposableStackPolyfill {
get disposed(): boolean;
async disposeAsync(): Promise<void>;
use<T extends AsyncDisposable | Disposable | null | undefined>(value: T): T;
adopt<T>(value: T, onDispose: (value: T) => Promise<void>): T;
defer(onDispose: () => Promise<void>): void;
move(): AsyncDisposableStackPolyfill;
[asyncDisposeSymbol](): Promise<void>;
}
export const DisposableStack: typeof DisposableStackPolyfill;
export const AsyncDisposableStack: typeof AsyncDisposableStackPolyfill;
export const SuppressedError: typeof SuppressedErrorPolyfill;
Import
import {disposeSymbol, asyncDisposeSymbol, DisposableStack, AsyncDisposableStack} from '../util/disposable.js';
I/O Contract
| Method | Parameter | Type | Description |
|---|---|---|---|
| use | value | null | undefined | A disposable resource to track (null/undefined are passthrough) |
| adopt | value | T |
A non-disposable resource to track |
| adopt | onDispose | (value: T) => void |
Callback invoked with the value during disposal |
| defer | onDispose | () => void |
Callback invoked during disposal |
| Method | Return Type | Description |
|---|---|---|
| use | T |
The same resource, now tracked by the stack |
| adopt | T |
The same resource, now tracked by the stack |
| move | DisposableStackPolyfill |
A new stack containing all resources; original is marked disposed |
| dispose / [Symbol.dispose] | void |
Disposes all tracked resources in LIFO order |
Usage Examples
// Using DisposableStack with the 'using' syntax
{
using stack = new DisposableStack();
const resource1 = stack.use(acquireResource());
const resource2 = stack.use(acquireAnotherResource());
// Both resources are disposed in reverse order when the block exits
}
// Using AsyncDisposableStack for async cleanup
{
await using stack = new AsyncDisposableStack();
stack.defer(async () => {
await cleanupConnection();
});
// Cleanup runs when stack is disposed
}
// Moving resources out of a stack (e.g., in a constructor)
class Connection {
#disposables: DisposableStack;
constructor() {
using stack = new DisposableStack();
stack.use(createTransport());
this.#disposables = stack.move();
}
[Symbol.dispose]() {
this.#disposables.dispose();
}
}