Enhanced AbortController with Node.js-style patterns for modern applications
npm install @nodelibraries/enhanced-abort-controller



> Enhanced AbortController with Node.js-style patterns for modern TypeScript applications.
This library provides an enhanced version of the native AbortController with additional features for modern Node.js applications:
- abort(reason?: string) - Immediately abort the operation with optional reason
- abortAfter(ms: number) - Abort after a specified delay in milliseconds
- abortAfterTimeSpan(timeSpan: TimeSpan) - Abort using TimeSpan objects
- dispose() - Dispose resources and abort
- reason - Get the abort reason
- Static methods for creating timeout controllers and linked controllers
- register(callback) - Register a callback with AbortRegistration
- throwIfAborted(message?: string) - Throw AbortError if aborted
- whenAborted - Promise that resolves when signal is aborted
- canBeAborted - Check if signal can be aborted
- Static properties for common signal patterns
- Static methods for timeout and signal combination
- Time interval representation for precise time management
- Multiple unit constructors (milliseconds, seconds, minutes, hours, days)
- Total value properties for different time units
- Static properties (zero, maxValue, minValue)
- unregister() - Unregister the callback
- dispose() - Dispose the registration
- isDisposed - Check if registration is disposed
- AbortError - Error thrown when operation is aborted
- Custom error messages support
- Proper error inheritance from native Error class
``bash`
npm install @nodelibraries/enhanced-abort-controller
This library is written in TypeScript and includes full type definitions. No additional @types package is required.
`typescript
import {
EnhancedAbortController,
EnhancedAbortSignal,
AbortError,
} from '@nodelibraries/enhanced-abort-controller';
// Create a controller
const controller = new EnhancedAbortController();
// Register a callback
const registration = controller.signal.register(() => {
console.log('โ ๏ธ Operation was aborted!');
});
// Abort after 5 seconds
controller.abortAfter(5000);
// Use in async operations
async function doWork(signal: EnhancedAbortSignal) {
for (let i = 0; i < 10; i++) {
signal.throwIfAborted();
console.log(Working... ${i});
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}
doWork(controller.signal).catch((err) => {
if (err instanceof AbortError) {
console.log('Operation was aborted');
}
});
// Clean up
registration.unregister();
`
`typescript
import {
EnhancedAbortController,
TimeSpan,
} from '@nodelibraries/enhanced-abort-controller';
const controller = new EnhancedAbortController();
// Abort after 3 seconds using milliseconds
controller.abortAfter(3000);
// Or using TimeSpan
const timeSpan = TimeSpan.fromSeconds(3);
controller.abortAfterTimeSpan(timeSpan);
// Static timeout controller
const timeoutController = EnhancedAbortController.timeout(5000);
`
`typescript
import { EnhancedAbortController } from '@nodelibraries/enhanced-abort-controller';
const controller1 = new EnhancedAbortController();
const controller2 = new EnhancedAbortController();
const controller3 = new EnhancedAbortController();
// Create a controller that aborts when any of the signals abort
const linkedController = EnhancedAbortController.linkSignals(
controller1.signal,
controller2.signal,
controller3.signal
);
linkedController.signal.register(() => {
console.log('๐ Any of the linked signals was aborted!');
});
// Abort one of them
controller1.abort('User cancelled');
`
The main controller class that manages abort operations.
#### Constructor
`typescript`
new EnhancedAbortController();
#### Instance Methods
##### abort(reason?: string)
Immediately aborts the controller with an optional reason.
`typescript`
const controller = new EnhancedAbortController();
controller.abort('User cancelled the operation');
##### abortAfter(ms: number)
Aborts the controller after the specified number of milliseconds.
`typescript`
const controller = new EnhancedAbortController();
controller.abortAfter(5000); // Abort after 5 seconds
##### abortAfterTimeSpan(timeSpan: TimeSpan)
Aborts the controller using a TimeSpan object.
`typescript
import { TimeSpan } from '@nodelibraries/enhanced-abort-controller';
const controller = new EnhancedAbortController();
const timeSpan = TimeSpan.fromMinutes(2.5);
controller.abortAfterTimeSpan(timeSpan);
`
##### dispose()
Disposes the controller and aborts it. After disposal, the controller cannot be reset.
`typescript`
const controller = new EnhancedAbortController();
controller.dispose();
#### Instance Properties
##### signal: EnhancedAbortSignal
The abort signal associated with this controller.
`typescript`
const controller = new EnhancedAbortController();
const signal = controller.signal;
##### isAborted: boolean
Whether the controller is currently aborted.
`typescript`
const controller = new EnhancedAbortController();
console.log(controller.isAborted); // false
controller.abort();
console.log(controller.isAborted); // true
##### isDisposed: boolean
Whether the controller has been disposed.
`typescript`
const controller = new EnhancedAbortController();
console.log(controller.isDisposed); // false
controller.dispose();
console.log(controller.isDisposed); // true
##### reason: string | undefined
The reason for the abort, if any.
`typescript`
const controller = new EnhancedAbortController();
controller.abort('User cancelled');
console.log(controller.reason); // "User cancelled"
#### Static Methods
##### linkSignals(...signals: EnhancedAbortSignal[]): EnhancedAbortController
Creates a controller that aborts when any of the provided signals abort.
`typescript
const controller1 = new EnhancedAbortController();
const controller2 = new EnhancedAbortController();
const linkedController = EnhancedAbortController.linkSignals(
controller1.signal,
controller2.signal
);
`
##### createLinkedController(...signals: EnhancedAbortSignal[]): EnhancedAbortController
Alias for linkSignals.
##### timeout(ms: number): EnhancedAbortController
Creates a controller that automatically aborts after the specified time.
`typescript`
const timeoutController = EnhancedAbortController.timeout(3000);
Enhanced abort signal with additional functionality.
#### Instance Methods
##### register(callback: () => void): AbortRegistration
Registers a callback to be called when the signal is aborted.
`typescript
const controller = new EnhancedAbortController();
const registration = controller.signal.register(() => {
console.log('Signal was aborted!');
});
// Later, unregister
registration.unregister();
`
##### throwIfAborted(message?: string): void
Throws an AbortError if the signal is aborted.
`typescript
const controller = new EnhancedAbortController();
try {
controller.signal.throwIfAborted('Custom message');
// Continue with work...
} catch (error) {
if (error instanceof AbortError) {
console.log('Operation was aborted:', error.message);
}
}
`
#### Instance Properties
##### isAborted: boolean
Whether the signal is currently aborted.
`typescript`
const controller = new EnhancedAbortController();
console.log(controller.signal.isAborted); // false
controller.abort();
console.log(controller.signal.isAborted); // true
##### reason: string | undefined
The reason for the abort, if any.
`typescript`
const controller = new EnhancedAbortController();
controller.abort('User cancelled');
console.log(controller.signal.reason); // "User cancelled"
##### canBeAborted: boolean
Whether the signal can be aborted.
`typescript`
const noneSignal = EnhancedAbortSignal.none;
console.log(noneSignal.canBeAborted); // false
##### whenAborted: Promise
A promise that resolves when the signal is aborted.
`typescript
const controller = new EnhancedAbortController();
controller.signal.whenAborted.then(() => {
console.log('Signal was aborted!');
});
// Later
controller.abort();
`
##### signal: AbortSignal
The underlying native AbortSignal.
`typescript`
const controller = new EnhancedAbortController();
const nativeSignal = controller.signal;
#### Static Properties
##### none: EnhancedAbortSignal
A signal that never aborts.
`typescript`
const neverAborting = EnhancedAbortSignal.none;
console.log(neverAborting.isAborted); // false
console.log(neverAborting.canBeAborted); // false
##### aborted: EnhancedAbortSignal
A signal that is already aborted.
`typescript`
const alreadyAborted = EnhancedAbortSignal.aborted;
console.log(alreadyAborted.isAborted); // true
#### Static Methods
##### timeout(ms: number): EnhancedAbortSignal
Creates a signal that automatically aborts after the specified time.
`typescript`
const timeoutSignal = EnhancedAbortSignal.timeout(2000);
##### any(signals: EnhancedAbortSignal[]): EnhancedAbortSignal
Creates a signal that aborts when any of the provided signals abort.
`typescript
const signal1 = new EnhancedAbortController();
const signal2 = new EnhancedAbortController();
const anySignal = EnhancedAbortSignal.any([signal1.signal, signal2.signal]);
`
A class for representing time intervals, inspired by .NET Core's TimeSpan.
#### Constructor
`typescript`
new TimeSpan(milliseconds: number)
#### Instance Properties
##### milliseconds: number
The total milliseconds of the time span.
`typescript`
const timeSpan = TimeSpan.fromSeconds(30);
console.log(timeSpan.milliseconds); // 30000
##### totalSeconds: number
The total seconds of the time span.
`typescript`
const timeSpan = TimeSpan.fromMinutes(2.5);
console.log(timeSpan.totalSeconds); // 150
##### totalMinutes: number
The total minutes of the time span.
`typescript`
const timeSpan = TimeSpan.fromHours(1.5);
console.log(timeSpan.totalMinutes); // 90
##### totalHours: number
The total hours of the time span.
`typescript`
const timeSpan = TimeSpan.fromDays(1.5);
console.log(timeSpan.totalHours); // 36
##### totalDays: number
The total days of the time span.
`typescript`
const timeSpan = TimeSpan.fromHours(48);
console.log(timeSpan.totalDays); // 2
#### Static Methods
##### fromMilliseconds(ms: number): TimeSpan
Creates a TimeSpan from milliseconds.
`typescript`
const timeSpan = TimeSpan.fromMilliseconds(1500);
##### fromSeconds(seconds: number): TimeSpan
Creates a TimeSpan from seconds.
`typescript`
const timeSpan = TimeSpan.fromSeconds(30);
##### fromMinutes(minutes: number): TimeSpan
Creates a TimeSpan from minutes.
`typescript`
const timeSpan = TimeSpan.fromMinutes(2.5);
##### fromHours(hours: number): TimeSpan
Creates a TimeSpan from hours.
`typescript`
const timeSpan = TimeSpan.fromHours(1.5);
##### fromDays(days: number): TimeSpan
Creates a TimeSpan from days.
`typescript`
const timeSpan = TimeSpan.fromDays(1.5);
#### Static Properties
##### zero: TimeSpan
A TimeSpan representing zero time.
`typescript`
console.log(TimeSpan.zero.milliseconds); // 0
##### maxValue: TimeSpan
A TimeSpan representing the maximum possible time.
`typescript`
console.log(TimeSpan.maxValue.milliseconds); // 9007199254740991
##### minValue: TimeSpan
A TimeSpan representing the minimum possible time.
`typescript`
console.log(TimeSpan.minValue.milliseconds); // -9007199254740991
Represents a registration for an abort signal callback.
#### Instance Methods
##### unregister(): void
Unregisters the callback from the signal.
`typescript
const controller = new EnhancedAbortController();
const registration = controller.signal.register(() => {
console.log('Aborted!');
});
registration.unregister();
`
##### dispose(): void
Disposes the registration (same as unregister).
`typescript
const registration = controller.signal.register(() => {
console.log('Aborted!');
});
registration.dispose();
`
#### Instance Properties
##### isDisposed: boolean
Whether the registration has been disposed.
`typescript`
const registration = controller.signal.register(() => {});
console.log(registration.isDisposed); // false
registration.unregister();
console.log(registration.isDisposed); // true
Error thrown when an operation is aborted.
#### Constructor
`typescript`
new AbortError(message?: string)
#### Usage
`typescript
import { AbortError } from '@nodelibraries/enhanced-abort-controller';
try {
signal.throwIfAborted('Custom message');
} catch (error) {
if (error instanceof AbortError) {
console.log('Operation was aborted:', error.message);
}
}
`
`typescript
import {
EnhancedAbortController,
EnhancedAbortSignal,
AbortError,
} from '@nodelibraries/enhanced-abort-controller';
async function complexWorkflow(signal: EnhancedAbortSignal) {
console.log('๐ Starting complex workflow...');
try {
// Step 1: Data preparation
signal.throwIfAborted();
console.log('๐ Step 1: Preparing data...');
await new Promise((resolve) => setTimeout(resolve, 500));
// Step 2: Data processing
signal.throwIfAborted();
console.log('โ๏ธ Step 2: Processing data...');
await new Promise((resolve) => setTimeout(resolve, 500));
// Step 3: Data validation
signal.throwIfAborted();
console.log('โ
Step 3: Validating data...');
await new Promise((resolve) => setTimeout(resolve, 500));
console.log('โ
Complex workflow completed successfully!');
} catch (error) {
if (error instanceof AbortError) {
console.log('โ๏ธ Complex workflow aborted:', error.message);
} else {
console.log('โ Complex workflow failed:', error);
}
}
}
const controller = new EnhancedAbortController();
complexWorkflow(controller.signal);
// Abort after 1 second
setTimeout(() => {
controller.abort('User cancelled workflow');
}, 1000);
`
`typescript
import {
EnhancedAbortController,
EnhancedAbortSignal,
AbortError,
} from '@nodelibraries/enhanced-abort-controller';
async function fetchWithAbort(url: string, signal: EnhancedAbortSignal) {
try {
const response = await fetch(url, {
signal: signal.signal, // Use native AbortSignal
});
if (!response.ok) {
throw new Error(HTTP error! status: ${response.status});
}
return await response.json();
} catch (error) {
if (error instanceof AbortError) {
console.log('Fetch was aborted:', error.message);
}
throw error;
}
}
const controller = new EnhancedAbortController();
controller.abortAfter(5000); // Abort after 5 seconds
fetchWithAbort('https://api.example.com/data', controller.signal).catch(() =>
console.log('Fetch completed')
);
`
`typescript
import {
EnhancedAbortController,
EnhancedAbortSignal,
AbortError,
} from '@nodelibraries/enhanced-abort-controller';
import axios from 'axios';
async function axiosWithAbort(url: string, signal: EnhancedAbortSignal) {
try {
const response = await axios.get(url, {
signal: signal.signal,
});
return response.data;
} catch (error) {
if (error instanceof AbortError) {
console.log('Axios request was aborted:', error.message);
}
throw error;
}
}
const controller = new EnhancedAbortController();
controller.abortAfter(3000);
axiosWithAbort('https://api.example.com/data', controller.signal).catch(() =>
console.log('Axios request completed')
);
`
`typescript
import { EnhancedAbortController } from '@nodelibraries/enhanced-abort-controller';
const controller = new EnhancedAbortController();
// Simulate resource allocation
let resources = ['Resource 1', 'Resource 2', 'Resource 3'];
console.log('๐ฆ Allocated resources:', resources);
const cleanupRegistration = controller.signal.register(() => {
// Cleanup resources
resources = [];
console.log('๐งน Resources cleaned up due to abort');
});
// Simulate work
setTimeout(() => {
console.log('โ
Work completed, cleaning up normally...');
cleanupRegistration.unregister();
resources = [];
console.log('๐งน Resources cleaned up normally');
}, 1500);
// Abort after 1 second (before normal completion)
setTimeout(() => {
controller.abort('Resource cleanup test');
}, 1000);
`
`typescript
import {
EnhancedAbortController,
TimeSpan,
} from '@nodelibraries/enhanced-abort-controller';
// Controller with immediate abort
const immediateController = new EnhancedAbortController();
immediateController.signal.register(() => {
console.log('โก Immediate controller aborted');
});
// Controller with timeout
const timeoutController = EnhancedAbortController.timeout(3000);
timeoutController.signal.register(() => {
console.log('โฐ Timeout controller aborted');
});
// Controller with TimeSpan
const timeSpanController = new EnhancedAbortController();
timeSpanController.abortAfterTimeSpan(TimeSpan.fromSeconds(2.5));
timeSpanController.signal.register(() => {
console.log('๐
TimeSpan controller aborted');
});
// Abort immediate controller after 1 second
setTimeout(() => {
immediateController.abort('Immediate abort');
}, 1000);
`
`typescript
import {
EnhancedAbortController,
EnhancedAbortSignal,
AbortError,
} from '@nodelibraries/enhanced-abort-controller';
const controller = new EnhancedAbortController();
// Pattern 1: Try-catch with throwIfAborted
async function pattern1(signal: EnhancedAbortSignal) {
try {
signal.throwIfAborted();
console.log('โ
Pattern 1: Operation completed');
} catch (error) {
if (error instanceof AbortError) {
console.log('โ๏ธ Pattern 1: Operation aborted');
}
}
}
// Pattern 2: Check isAborted before operations
async function pattern2(signal: EnhancedAbortSignal) {
if (signal.isAborted) {
console.log('โ๏ธ Pattern 2: Signal already aborted');
return;
}
console.log('โ
Pattern 2: Operation completed');
}
// Pattern 3: Use whenAborted promise
async function pattern3(signal: EnhancedAbortSignal) {
await signal.whenAborted;
console.log('โ๏ธ Pattern 3: Signal was aborted');
}
// Test patterns
pattern1(controller.signal);
pattern2(controller.signal);
pattern3(controller.signal);
// Abort after delay
setTimeout(() => {
controller.abort('Error pattern test');
}, 800);
`
The library includes comprehensive test coverage. Run the tests with:
`bash`
npm test
Run tests with coverage:
`bash`
npm run test:coverage
`bash`
npm run build
`bash`
npm run lint
`bash`
npm run dev
`bash`
npm run build
node dist/examples/showcases.js
This project includes GitHub Pages support for hosting documentation. The documentation is automatically deployed when changes are pushed to the main branch.
1. Enable GitHub Pages in your repository settings:
- Go to Settings โ Pages
- Source: Select "GitHub Actions"
2. Automatic Deployment:
- The .github/workflows/pages.yml workflow automatically builds and deploys documentationhttps://nodelibraries.github.io/enhanced-abort-controller
- Documentation is available at:
3. Manual Deployment:
- The workflow can also be triggered manually from the Actions tab
- docs/index.html - Main documentation page
- Automatically generated from the project build
- Includes API reference, examples, and usage patterns
To preview the documentation locally:
`bashBuild the project
npm run build
๐ค Contributing
1. Fork the repository
2. Create your feature branch (
git checkout -b feature/amazing-feature)
3. Commit your changes (git commit -m 'Add some amazing feature')
4. Push to the branch (git push origin feature/amazing-feature)
5. Open a Pull Request๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
๐ Acknowledgments
- Built on top of the native Web API
AbortController
- Designed for modern TypeScript and Node.js applications
- Node.js-style patterns for clean and efficient async operations๐ Documentation
Full API documentation is available at:
- GitHub Pages: https://nodelibraries.github.io/enhanced-abort-controller
- npm Package: https://www.npmjs.com/package/@nodelibraries/enhanced-abort-controller
๐ Links
- GitHub Repository
- npm Package
- TypeScript
- Node.js
๐ Project Status
- โ
Full TypeScript support with type definitions
- โ
Comprehensive test coverage
- โ
Zero dependencies
- โ
Compatible with Node.js 16+ and modern browsers
- โ
MIT License
๐ Quick Examples
$3
`typescript
import { EnhancedAbortController } from '@nodelibraries/enhanced-abort-controller';const controller = new EnhancedAbortController();
controller.abortAfter(5000); // Auto-abort after 5 seconds
// Use with fetch
fetch('https://api.example.com/data', {
signal: controller.signal.signal,
}).catch((err) => {
if (err.name === 'AbortError') {
console.log('Request was cancelled');
}
});
`$3
`typescript
import { EnhancedAbortController } from '@nodelibraries/enhanced-abort-controller';const userController = new EnhancedAbortController();
const timeoutController = EnhancedAbortController.timeout(10000);
// Abort if user cancels OR timeout occurs
const linked = EnhancedAbortController.linkSignals(
userController.signal,
timeoutController.signal
);
// Use linked signal
fetch('https://api.example.com/data', {
signal: linked.signal.signal,
});
`$3
`typescript
import {
EnhancedAbortController,
AbortError,
} from '@nodelibraries/enhanced-abort-controller';async function processData(signal: EnhancedAbortSignal) {
const resources: string[] = [];
try {
// Register cleanup
signal.register(() => {
console.log('Cleaning up resources...');
resources.length = 0;
});
// Process data
for (let i = 0; i < 100; i++) {
signal.throwIfAborted();
resources.push(
Resource ${i});
await processItem(i);
}
} catch (error) {
if (error instanceof AbortError) {
console.log('Processing was cancelled');
}
throw error;
}
}
``---
Made with โค๏ธ for the TypeScript and Node.js community