A library of utility functions for working with disposable objects
A lightweight utility library for managing resource lifecycles in the browser and Node.js. It leverages the new DisposableStack and using syntax to handle cleanup for DOM elements, Event Listeners, Blob URLs, and AbortControllers automatically.
npm install @aegisjsproject/disposableNote: This library relies on modern JavaScript features including
DisposableStack,Promise.withResolvers, andPromise.try. Ensure your environment supports them or provide polyfills.
- DOM Integration: Auto-remove elements and revoke Blob URLs when a scope closes.
- AbortSignal Synergy: Link
AbortControllerlifecycles directly to the disposal stack. - Async Scopes: Handle complex async flows with auto-cleanup on resolution or error.
- Safety Guards: Create revocable proxies that strictly enforce object access only within a specific scope.
Manage complex async workflows where every resource (network requests, DOM elements, Blob URLs) is cleaned up automatically when the task finishes or is aborted.
import { useAsyncDisposableStack, DisposableObjectURL, createDisposableElement } from '@aegisjsproject/disposable';
await useAsyncDisposableStack(async (stack, { signal }) => {
// 1. Fetch a resource (aborts if the stack closes early)
const resp = await fetch('/data.json', { signal });
const data = await resp.blob();
// 2. Create a Blob URL that revokes itself on dispose
// Note: DisposableObjectURL extends String, so it works directly in template literals
const url = stack.use(new DisposableObjectURL(data));
// 3. Create a temporary DOM element that removes itself on dispose
const dialog = createDisposableElement(stack, { tag: 'dialog' });
dialog.innerHTML = `<img src="${url}" />`;
document.body.append(dialog);
dialog.showModal();
// Keep the scope open for 5 seconds, then auto-cleanup everything
await new Promise(resolve => setTimeout(resolve, 5000));
});
// At this point: Dialog is removed, URL is revoked, fetch signal is aborted.Creates a synchronous scope. The stack is disposed immediately after the callback returns.
useDisposableStack((stack) => {
stack.defer(() => console.log('Cleanup'));
// Do work...
});Creates an asynchronous scope. The stack is disposed when the promise resolves, rejects, or if the optional signal is aborted.
signal: AnAbortSignalthat, if triggered, will immediately abort the scope and dispose of the stack.callbackargs:{ controller, signal, timeStamp, id }.
const controller = new AbortController();
await useAsyncDisposableStack(async (stack, { signal }) => {
// This signal combines the input signal and the stack's lifecycle
await fetch('/api', { signal });
}, { signal: controller.signal });Extends AbortController. When disposed, it automatically aborts its signal (if not already aborted).
{
using controller = new DisposableAbortController();
// controller will abort automatically at the end of this block
}Extends String. Wraps a Blob or File to create a URL that revokes itself upon disposal.
- Behaves like a string: Can be used in
img.srcor template literals. - Revocable: Calls
URL.revokeObjectURLon dispose.
{
using url = new DisposableObjectURL(blob);
img.src = url; // Works directly
// Revokes the object URL automatically
}A wrapper around scheduler.postTask that is integrated with disposal.
- Auto-aborts the task if the object is disposed before completion.
- Provides standard Promise methods (
then,catch,finally).
using task = new DisposableTask(() => heavyComputation(), { priority: 'background' });
// Task is aborted if not executed when leaving scopeCreates a DOM element that is automatically remove()'d from the DOM when the stack is disposed.
config.tag: The HTML tag name (default:'div').config.attrs: All other properties are set as attributes on the element.
using stack = new DisposableStack();
const spinner = createDisposableElement(stack, {
tag: 'div',
class: 'loader',
'aria-busy': 'true'
});
document.body.append(spinner);
// Spinner is removed from DOM automatically when stack disposesCreates a Proxy that becomes unusable (throws TypeError) once the stack is disposed. Useful for preventing memory leaks or access to stale objects.
const state = { sensitive: true };
const safeState = guard(stack, state);
// safeState is usable here
// ... stack disposes ...
console.log(safeState.sensitive); // Throws TypeErrorReturns a Promise<void> that resolves when the provided stack is disposed.
const stack = new DisposableStack();
whenDisposed(stack).then(() => {
console.log('Stack is now clean');
});
stack.dispose();