AwaitQueue
AwaitQueue is a TypeScript-first utility designed to manage and execute asynchronous tasks sequentially within Node.js and browser environments. Currently stable at version 3.3.0, it provides a robust mechanism to enqueue `Promise`-returning or async functions, ensuring they run one after another, preventing concurrency issues. Key features include task naming, the ability to remove specific pending tasks, and a `stop` mechanism that rejects all currently pending tasks with custom error types. Its primary differentiator lies in its straightforward API for controlling task flow, particularly useful in scenarios requiring strict ordering, such as database writes, API calls with rate limits, or resource-intensive operations.
Common errors
-
TypeError: AwaitQueue is not a constructor
cause Attempting to import `AwaitQueue` incorrectly in CommonJS, e.g., `const AwaitQueue = require('awaitqueue');` which expects a default export, but `awaitqueue` uses named exports. Or trying to use `require` in an ES Module context.fixFor CommonJS, use `const { AwaitQueue } = require('awaitqueue');`. For ES Modules, ensure your `package.json` specifies `"type": "module"` or your file has a `.mjs` extension, and use `import { AwaitQueue } from 'awaitqueue';`. -
UnhandledPromiseRejectionWarning: AwaitQueueStoppedError: Queue stopped
cause A task was enqueued and then the `awaitQueue.stop()` method was called, but the promise returned by `awaitQueue.push()` was not caught.fixEnsure all calls to `awaitQueue.push()` are followed by a `.catch()` block or are `await`ed within a `try...catch` block, explicitly handling `AwaitQueueStoppedError`. -
This expression is not callable. Type 'unknown' has no call signatures. Did you mean to call it with 'new'?
cause Passing a non-function or an invalid function type to `awaitQueue.push()`. The `task` argument must be a function that returns a value or a Promise.fixThe `task` argument for `awaitQueue.push()` must be a function, typically an `async` function or one that returns a `Promise`. Ensure you are passing `() => Promise<T>` or `async () => T`.
Warnings
- breaking Version 3.x of `awaitqueue` requires Node.js version 20 or higher. Older Node.js environments (e.g., Node.js 18 or lower) are not supported and will lead to runtime errors or unexpected behavior.
- gotcha When tasks are removed via `awaitQueue.remove()` or the entire queue is stopped via `awaitQueue.stop()`, the `push()` call for those tasks will reject with specific error instances: `AwaitQueueRemovedTaskError` and `AwaitQueueStoppedError`, respectively. Failing to catch these specific errors can result in unhandled promise rejections.
- gotcha Using the `removeOngoingTasksWithSameName: true` option in `awaitQueue.push()` will implicitly reject any already enqueued tasks that share the same name. This behavior might be unexpected if not explicitly accounted for, leading to tasks being prematurely cancelled.
Install
-
npm install awaitqueue -
yarn add awaitqueue -
pnpm add awaitqueue
Imports
- AwaitQueue
const AwaitQueue = require('awaitqueue');import { AwaitQueue } from 'awaitqueue'; - AwaitQueuePushOptions
import { AwaitQueuePushOptions } from 'awaitqueue';import type { AwaitQueuePushOptions } from 'awaitqueue'; - AwaitQueueStoppedError
const AwaitQueueStoppedError = require('awaitqueue').AwaitQueueStoppedError;import { AwaitQueueStoppedError } from 'awaitqueue';
Quickstart
import { AwaitQueue, AwaitQueueStoppedError, AwaitQueueRemovedTaskError } from 'awaitqueue';
async function runTasks() {
const queue = new AwaitQueue();
console.log('Queue created. Size:', queue.size);
// Task 1: A simple resolving task
queue.push(async () => {
console.log('Task 1: Starting (2s)');
await new Promise(resolve => setTimeout(resolve, 2000));
console.log('Task 1: Completed');
return 'Result 1';
}, 'task-one').then(res => console.log(`Task 1 resolved with: ${res}`)).catch(err => console.error(`Task 1 failed: ${err.message}`));
// Task 2: A task that takes longer
queue.push(async () => {
console.log('Task 2: Starting (5s)');
await new Promise(resolve => setTimeout(resolve, 5000));
console.log('Task 2: Completed');
return 'Result 2';
}, 'task-two').then(res => console.log(`Task 2 resolved with: ${res}`)).catch(err => console.error(`Task 2 failed: ${err.message}`));
// Task 3: A task to be removed
const task3Promise = queue.push(async () => {
console.log('Task 3: Should not run');
return 'Result 3';
}, 'task-three');
task3Promise.then(res => console.log(`Task 3 resolved with: ${res}`)).catch(err => {
if (err instanceof AwaitQueueRemovedTaskError) {
console.warn(`Task 3 was removed: ${err.message}`);
} else {
console.error(`Task 3 failed: ${err.message}`);
}
});
// Task 4: A task to be stopped (after Task 2, but before its completion)
const task4Promise = queue.push(async () => {
console.log('Task 4: Should be stopped');
await new Promise(resolve => setTimeout(resolve, 1000));
return 'Result 4';
}, 'task-four');
task4Promise.then(res => console.log(`Task 4 resolved with: ${res}`)).catch(err => {
if (err instanceof AwaitQueueStoppedError) {
console.warn(`Task 4 was stopped: ${err.message}`);
} else {
console.error(`Task 4 failed: ${err.message}`);
}
});
console.log('Initial queue size:', queue.size);
console.log('Dumping queue:', queue.dump());
// Simulate actions after some time
await new Promise(resolve => setTimeout(resolve, 3000)); // Wait for Task 1 to finish and Task 2 to start
console.log('Removing Task 3 (index 1 in 0-indexed dump after Task 1 ran)');
queue.remove(1); // Task 3 is at index 1 after Task 1 finishes and Task 2 is at index 0
await new Promise(resolve => setTimeout(resolve, 2000)); // Wait a bit more
console.log('Stopping the queue. All remaining pending tasks will be rejected.');
queue.stop();
console.log('Final queue size:', queue.size);
console.log('Dumping queue after stop:', queue.dump());
}
runTasks().catch(console.error);