@nodelibraries/enhanced-abort-controller
v1.1.0
Published
Enhanced AbortController with Node.js-style patterns for modern applications
Maintainers
Readme
Enhanced Abort Controller
Enhanced AbortController with Node.js-style patterns for modern TypeScript applications.
🚀 Features
This library provides an enhanced version of the native AbortController with additional features for modern Node.js applications:
✨ EnhancedAbortController
abort(reason?: string)- Immediately abort the operation with optional reasonabortAfter(ms: number)- Abort after a specified delay in millisecondsabortAfterTimeSpan(timeSpan: TimeSpan)- Abort using TimeSpan objectsdispose()- Dispose resources and abortreason- Get the abort reasonStatic methods for creating timeout controllers and linked controllers
✨ EnhancedAbortSignal
register(callback)- Register a callback with AbortRegistrationthrowIfAborted(message?: string)- Throw AbortError if abortedwhenAborted- Promise that resolves when signal is abortedcanBeAborted- Check if signal can be abortedStatic properties for common signal patterns
Static methods for timeout and signal combination
✨ TimeSpan
- 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)
✨ AbortRegistration
unregister()- Unregister the callbackdispose()- Dispose the registrationisDisposed- Check if registration is disposed
✨ Error Handling
AbortError- Error thrown when operation is aborted- Custom error messages support
- Proper error inheritance from native Error class
📦 Installation
npm install @nodelibraries/enhanced-abort-controllerTypeScript Support
This library is written in TypeScript and includes full type definitions. No additional @types package is required.
🎯 Quick Start
Basic Usage
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();Timeout-based Abortion
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);Linked Controllers
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');📚 API Reference
EnhancedAbortController
The main controller class that manages abort operations.
Constructor
new EnhancedAbortController();Instance Methods
abort(reason?: string)
Immediately aborts the controller with an optional reason.
const controller = new EnhancedAbortController();
controller.abort('User cancelled the operation');abortAfter(ms: number)
Aborts the controller after the specified number of milliseconds.
const controller = new EnhancedAbortController();
controller.abortAfter(5000); // Abort after 5 secondsabortAfterTimeSpan(timeSpan: TimeSpan)
Aborts the controller using a TimeSpan object.
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.
const controller = new EnhancedAbortController();
controller.dispose();Instance Properties
signal: EnhancedAbortSignal
The abort signal associated with this controller.
const controller = new EnhancedAbortController();
const signal = controller.signal;isAborted: boolean
Whether the controller is currently aborted.
const controller = new EnhancedAbortController();
console.log(controller.isAborted); // false
controller.abort();
console.log(controller.isAborted); // trueisDisposed: boolean
Whether the controller has been disposed.
const controller = new EnhancedAbortController();
console.log(controller.isDisposed); // false
controller.dispose();
console.log(controller.isDisposed); // truereason: string | undefined
The reason for the abort, if any.
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.
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.
const timeoutController = EnhancedAbortController.timeout(3000);EnhancedAbortSignal
Enhanced abort signal with additional functionality.
Instance Methods
register(callback: () => void): AbortRegistration
Registers a callback to be called when the signal is aborted.
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.
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.
const controller = new EnhancedAbortController();
console.log(controller.signal.isAborted); // false
controller.abort();
console.log(controller.signal.isAborted); // truereason: string | undefined
The reason for the abort, if any.
const controller = new EnhancedAbortController();
controller.abort('User cancelled');
console.log(controller.signal.reason); // "User cancelled"canBeAborted: boolean
Whether the signal can be aborted.
const noneSignal = EnhancedAbortSignal.none;
console.log(noneSignal.canBeAborted); // falsewhenAborted: Promise<void>
A promise that resolves when the signal is aborted.
const controller = new EnhancedAbortController();
controller.signal.whenAborted.then(() => {
console.log('Signal was aborted!');
});
// Later
controller.abort();signal: AbortSignal
The underlying native AbortSignal.
const controller = new EnhancedAbortController();
const nativeSignal = controller.signal;Static Properties
none: EnhancedAbortSignal
A signal that never aborts.
const neverAborting = EnhancedAbortSignal.none;
console.log(neverAborting.isAborted); // false
console.log(neverAborting.canBeAborted); // falseaborted: EnhancedAbortSignal
A signal that is already aborted.
const alreadyAborted = EnhancedAbortSignal.aborted;
console.log(alreadyAborted.isAborted); // trueStatic Methods
timeout(ms: number): EnhancedAbortSignal
Creates a signal that automatically aborts after the specified time.
const timeoutSignal = EnhancedAbortSignal.timeout(2000);any(signals: EnhancedAbortSignal[]): EnhancedAbortSignal
Creates a signal that aborts when any of the provided signals abort.
const signal1 = new EnhancedAbortController();
const signal2 = new EnhancedAbortController();
const anySignal = EnhancedAbortSignal.any([signal1.signal, signal2.signal]);TimeSpan
A class for representing time intervals, inspired by .NET Core's TimeSpan.
Constructor
new TimeSpan(milliseconds: number)Instance Properties
milliseconds: number
The total milliseconds of the time span.
const timeSpan = TimeSpan.fromSeconds(30);
console.log(timeSpan.milliseconds); // 30000totalSeconds: number
The total seconds of the time span.
const timeSpan = TimeSpan.fromMinutes(2.5);
console.log(timeSpan.totalSeconds); // 150totalMinutes: number
The total minutes of the time span.
const timeSpan = TimeSpan.fromHours(1.5);
console.log(timeSpan.totalMinutes); // 90totalHours: number
The total hours of the time span.
const timeSpan = TimeSpan.fromDays(1.5);
console.log(timeSpan.totalHours); // 36totalDays: number
The total days of the time span.
const timeSpan = TimeSpan.fromHours(48);
console.log(timeSpan.totalDays); // 2Static Methods
fromMilliseconds(ms: number): TimeSpan
Creates a TimeSpan from milliseconds.
const timeSpan = TimeSpan.fromMilliseconds(1500);fromSeconds(seconds: number): TimeSpan
Creates a TimeSpan from seconds.
const timeSpan = TimeSpan.fromSeconds(30);fromMinutes(minutes: number): TimeSpan
Creates a TimeSpan from minutes.
const timeSpan = TimeSpan.fromMinutes(2.5);fromHours(hours: number): TimeSpan
Creates a TimeSpan from hours.
const timeSpan = TimeSpan.fromHours(1.5);fromDays(days: number): TimeSpan
Creates a TimeSpan from days.
const timeSpan = TimeSpan.fromDays(1.5);Static Properties
zero: TimeSpan
A TimeSpan representing zero time.
console.log(TimeSpan.zero.milliseconds); // 0maxValue: TimeSpan
A TimeSpan representing the maximum possible time.
console.log(TimeSpan.maxValue.milliseconds); // 9007199254740991minValue: TimeSpan
A TimeSpan representing the minimum possible time.
console.log(TimeSpan.minValue.milliseconds); // -9007199254740991AbortRegistration
Represents a registration for an abort signal callback.
Instance Methods
unregister(): void
Unregisters the callback from the signal.
const controller = new EnhancedAbortController();
const registration = controller.signal.register(() => {
console.log('Aborted!');
});
registration.unregister();dispose(): void
Disposes the registration (same as unregister).
const registration = controller.signal.register(() => {
console.log('Aborted!');
});
registration.dispose();Instance Properties
isDisposed: boolean
Whether the registration has been disposed.
const registration = controller.signal.register(() => {});
console.log(registration.isDisposed); // false
registration.unregister();
console.log(registration.isDisposed); // trueAbortError
Error thrown when an operation is aborted.
Constructor
new AbortError(message?: string)Usage
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);
}
}🔧 Advanced Usage Examples
Complex Async Workflow
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);🌐 Library Integration Examples
Fetch API Integration
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')
);Axios Integration
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')
);Resource Cleanup Pattern
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);Multiple Controllers with Different Strategies
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);Error Handling Patterns
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);🧪 Testing
The library includes comprehensive test coverage. Run the tests with:
npm testRun tests with coverage:
npm run test:coverage📝 Development
Building
npm run buildLinting
npm run lintDevelopment Mode
npm run devRunning Examples
npm run build
node dist/examples/showcases.js🌐 GitHub Pages
This project includes GitHub Pages support for hosting documentation. The documentation is automatically deployed when changes are pushed to the main branch.
Setup
Enable GitHub Pages in your repository settings:
- Go to Settings → Pages
- Source: Select "GitHub Actions"
Automatic Deployment:
- The
.github/workflows/pages.ymlworkflow automatically builds and deploys documentation - Documentation is available at:
https://nodelibraries.github.io/enhanced-abort-controller
- The
Manual Deployment:
- The workflow can also be triggered manually from the Actions tab
Documentation Structure
docs/index.html- Main documentation page- Automatically generated from the project build
- Includes API reference, examples, and usage patterns
Local Preview
To preview the documentation locally:
# Build the project
npm run build
# Serve the docs directory
npx serve docs
# or
python -m http.server 8000 -d docs🤝 Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - 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
📊 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
Basic Timeout
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');
}
});Linked Controllers
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,
});Async Workflow with Cleanup
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
