p-abort
Abortable step-by-step operations with automatic cleanup.
Installation
npm install p-abort
pnpm add p-abort
yarn add p-abort
Overview
p-abort provides a utility for creating abortable asynchronous operations that can be cleanly cancelled at any step. It's particularly useful for:
- Long-running async operations that need cancellation support
- Multi-step workflows where intermediate steps should be skippable
- Resource cleanup when operations are aborted
- Preventing race conditions in async code
Usage
Basic Example
import { abortable } from 'p-abort';
const operation = abortable($ => async (id: string) => {
const user = await $(fetchUser(id));
const posts = await $(fetchPosts(user.id));
const processed = await $(processPosts(posts));
return processed;
});
const promise = operation('user123');
setTimeout(() => {
promise.abort();
}, 1000);
const result = await promise;
if (result.ok) {
console.log('Success:', result.data);
} else {
console.log('Operation was aborted');
}
With Cleanup
import { abortable } from 'p-abort';
const uploadWithProgress = abortable($ => async (file: File) => {
const controller = new AbortController();
$.cleanup(() => {
controller.abort();
console.log('Upload cancelled, cleaning up...');
});
const response = await $(uploadFile(file, { signal: controller.signal }));
return response;
});
const upload = uploadWithProgress(myFile);
upload.abort();
Function Wrapping
The $ utility can also wrap functions to make them abort-aware:
import { abortable } from 'p-abort';
const processData = abortable($ => async (items: string[]) => {
const abortableFetch = $(fetchData);
const results = [];
for (const item of items) {
const data = await abortableFetch(item);
results.push(data);
}
return results;
});
Parallel Operations
Use $.all() to run multiple operations in parallel while maintaining abort capability:
const parallelFetch = abortable($ => async (ids: string[]) => {
const promises = ids.map(id => $(fetchData(id)));
const results = await $.all(promises);
return results;
});
API
abortable(fn)
Creates an abortable operation from a function that receives an abort-aware utility.
Parameters
fn: A function that receives the $ utility and returns an async function
Returns
A function that returns a Promise with an additional abort() method.
Promise Result
The returned promise resolves to:
{ ok: true, data: T } on successful completion
{ ok: false } if the operation was aborted
$ Utility
The $ utility provides several methods for making operations abort-aware:
$(promise)
Wraps a promise or value to be abort-aware. If the operation is aborted before completion, it throws an AbortedError.
$(fn)
Wraps a function to make it abort-aware. The wrapped function will check for abortion before and after execution.
$.cleanup(fn)
Registers a cleanup function to be called when the operation completes or is aborted. Cleanup functions are executed in reverse order of registration (LIFO). Useful for resource cleanup.
$.all(array)
Wraps multiple promises in a Promise.all-like operation. All promises become abort-aware and the entire operation can be cancelled.
$.abort()
Manually aborts the current operation from within the operation function.
runAbortable(fn)
Creates and immediately runs an abortable operation without parameters.
Parameters
fn: A function that receives the $ utility and returns a value or promise
Returns
An AbortableTask that can be aborted and resolves to a result object.
import { runAbortable } from 'p-abort';
const task = runAbortable(async $ => {
const data = await $(fetchData());
return data;
});
task.abort();
const result = await task;
if (result.ok) {
console.log('Result:', result.data);
} else {
console.log('Task was aborted');
}
Error Handling
When an operation is aborted, any in-progress steps are cancelled and the promise resolves to { ok: false }. Other errors are propagated normally. You don't need to manually handle AbortedError as it's handled internally.
Advanced Usage
Chaining Operations
const complexWorkflow = abortable($ => async (input: string) => {
const validated = await $(validateInput(input));
const transformed = await $(transformData(validated));
const saved = await $(retry(() => saveData(transformed), 3));
await $(sendNotification(saved));
return saved;
});
Resource Management
const fileProcessor = abortable($ => async (filePath: string) => {
const file = await $(openFile(filePath));
$.cleanup(() => {
file.close();
console.log('File closed due to abort');
});
const content = await $(readFile(file));
const processed = await $(processContent(content));
return processed;
});
TypeScript Support
This package is written in TypeScript and provides full type safety:
interface UserData {
id: string;
name: string;
}
const getUser = abortable($ => async (id: string): Promise<UserData> => {
const response = await $(fetch(`/api/users/${id}`));
return response.json();
});
License
MIT © RanolP