Skip to content

Commit

Permalink
Require Node.js 18
Browse files Browse the repository at this point in the history
  • Loading branch information
sindresorhus committed Dec 7, 2023
1 parent ce0c364 commit 68dfe95
Show file tree
Hide file tree
Showing 10 changed files with 58 additions and 73 deletions.
2 changes: 0 additions & 2 deletions .github/funding.yml

This file was deleted.

4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ jobs:
node-version:
- 16
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm install
Expand Down
8 changes: 4 additions & 4 deletions bench.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Benchmark, {Deferred, Event} from 'benchmark';
import Benchmark, {type Deferred, type Event} from 'benchmark';
import PQueue from './source/index.js';

const suite = new Benchmark.Suite();
Expand All @@ -10,7 +10,7 @@ suite
.add('baseline', {
defer: true,

fn: async (deferred: Resolvable) => {
async fn(deferred: Resolvable) {
const queue = new PQueue();

for (let i = 0; i < 100; i++) {
Expand All @@ -25,7 +25,7 @@ suite
.add('operation with random priority', {
defer: true,

fn: async (deferred: Resolvable) => {
async fn(deferred: Resolvable) {
const queue = new PQueue();

for (let i = 0; i < 100; i++) {
Expand All @@ -42,7 +42,7 @@ suite
.add('operation with increasing priority', {
defer: true,

fn: async (deferred: Resolvable) => {
async fn(deferred: Resolvable) {
const queue = new PQueue();

for (let i = 0; i < 100; i++) {
Expand Down
38 changes: 21 additions & 17 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@
"repository": "sindresorhus/p-queue",
"funding": "https://github.com/sponsors/sindresorhus",
"type": "module",
"exports": "./dist/index.js",
"exports": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"sideEffects": false,
"engines": {
"node": ">=12"
"node": ">=18"
},
"scripts": {
"build": "del-cli dist && tsc",
"//test": "xo && ava && del-cli dist && tsc && tsd",
"test": "ava && del-cli dist && tsc && tsd",
"bench": "node --loader=ts-node/esm bench.ts",
"test": "xo && ava && del-cli dist && tsc && tsd",
"bench": "node --import=tsx/esm bench.ts",
"prepublishOnly": "del-cli dist && tsc"
},
"files": [
Expand Down Expand Up @@ -45,34 +48,35 @@
],
"dependencies": {
"eventemitter3": "^5.0.1",
"p-timeout": "^5.0.2"
"p-timeout": "^6.1.2"
},
"devDependencies": {
"@sindresorhus/tsconfig": "^2.0.0",
"@types/benchmark": "^2.1.1",
"@types/node": "^17.0.13",
"@sindresorhus/tsconfig": "^5.0.0",
"@types/benchmark": "^2.1.5",
"@types/node": "^20.10.4",
"ava": "^5.3.1",
"benchmark": "^2.1.4",
"del-cli": "^5.0.0",
"delay": "^5.0.0",
"del-cli": "^5.1.0",
"delay": "^6.0.0",
"in-range": "^3.0.0",
"p-defer": "^4.0.0",
"random-int": "^3.0.0",
"time-span": "^5.0.0",
"ts-node": "^10.9.1",
"tsd": "^0.25.0",
"typescript": "^5.2.2",
"xo": "^0.52.0"
"time-span": "^5.1.0",
"tsd": "^0.29.0",
"tsx": "^4.6.2",
"typescript": "^5.3.3",
"xo": "^0.56.0"
},
"ava": {
"workerThreads": false,
"files": [
"test/**"
],
"extensions": {
"ts": "module"
},
"nodeArguments": [
"--loader=ts-node/esm"
"--import=tsx/esm"
]
},
"xo": {
Expand Down
14 changes: 4 additions & 10 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Note that the project is feature complete. We are happy to review pull requests,
npm install p-queue
```

**Warning:** This package is native [ESM](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) and no longer provides a CommonJS export. If your project uses CommonJS, you'll have to [convert to ESM](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) or use the [dynamic `import()`](https://v8.dev/features/dynamic-import) function. Please don't open issues for questions regarding CommonJS / ESM.
**Warning:** This package is native [ESM](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) and no longer provides a CommonJS export. If your project uses CommonJS, you'll have to [convert to ESM](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c). Please don't open issues for questions regarding CommonJS / ESM.

## Usage

Expand Down Expand Up @@ -139,12 +139,10 @@ Priority of operation. Operations with greater priority will be scheduled first.

##### signal

*Requires Node.js 16 or later.*

[`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) for cancellation of the operation. When aborted, it will be removed from the queue and the `queue.add()` call will reject with an `AbortError`. If the operation is already running, the signal will need to be handled by the operation itself.
[`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) for cancellation of the operation. When aborted, it will be removed from the queue and the `queue.add()` call will reject with an [error](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/reason). If the operation is already running, the signal will need to be handled by the operation itself.

```js
import PQueue, {AbortError} from 'p-queue';
import PQueue from 'p-queue';
import got, {CancelError} from 'got';

const queue = new PQueue();
Expand All @@ -168,7 +166,7 @@ try {
}
}, {signal: controller.signal});
} catch (error) {
if (!(error instanceof AbortError)) {
if (!(error instanceof DOMException)) {
throw error;
}
}
Expand Down Expand Up @@ -378,10 +376,6 @@ await queue.add(() => delay(600));
//=> 'Task is completed. Size: 0 Pending: 0'
```

### AbortError

The error thrown by `queue.add()` when a job is aborted before it is run. See [`signal`](#signal).

## Advanced example

A more advanced example to help you understand the flow.
Expand Down
29 changes: 9 additions & 20 deletions source/index.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
import {EventEmitter} from 'eventemitter3';
import pTimeout, {TimeoutError} from 'p-timeout';
import {Queue, RunFunction} from './queue.js';
import {type Queue, type RunFunction} from './queue.js';
import PriorityQueue from './priority-queue.js';
import {QueueAddOptions, Options, TaskOptions} from './options.js';
import {type QueueAddOptions, type Options, type TaskOptions} from './options.js';

type Task<TaskResultType> =
| ((options: TaskOptions) => PromiseLike<TaskResultType>)
| ((options: TaskOptions) => TaskResultType);

/**
The error thrown by `queue.add()` when a job is aborted before it is run. See `signal`.
*/
export class AbortError extends Error {}

type EventName = 'active' | 'idle' | 'empty' | 'add' | 'next' | 'completed' | 'error';

/**
Promise queue with concurrency control.
*/
export default class PQueue<QueueType extends Queue<RunFunction, EnqueueOptionsType> = PriorityQueue, EnqueueOptionsType extends QueueAddOptions = QueueAddOptions> extends EventEmitter<EventName> {
export default class PQueue<QueueType extends Queue<RunFunction, EnqueueOptionsType> = PriorityQueue, EnqueueOptionsType extends QueueAddOptions = QueueAddOptions> extends EventEmitter<EventName> { // eslint-disable-line @typescript-eslint/naming-convention, unicorn/prefer-event-target
readonly #carryoverConcurrencyCount: boolean;

readonly #isIntervalIgnored: boolean;
Expand Down Expand Up @@ -228,9 +223,7 @@ export default class PQueue<QueueType extends Queue<RunFunction, EnqueueOptionsT
async #throwOnAbort(signal: AbortSignal): Promise<never> {
return new Promise((_resolve, reject) => {
signal.addEventListener('abort', () => {
// TODO: Reject with signal.throwIfAborted() when targeting Node.js 18
// TODO: Use ABORT_ERR code when targeting Node.js 16 (https://nodejs.org/docs/latest-v16.x/api/errors.html#abort_err)
reject(new AbortError('The task was aborted.'));
reject(signal.reason);
}, {once: true});
});
}
Expand All @@ -253,16 +246,12 @@ export default class PQueue<QueueType extends Queue<RunFunction, EnqueueOptionsT
this.#intervalCount++;

try {
// TODO: Use options.signal?.throwIfAborted() when targeting Node.js 18
if (options.signal?.aborted) {
// TODO: Use ABORT_ERR code when targeting Node.js 16 (https://nodejs.org/docs/latest-v16.x/api/errors.html#abort_err)
throw new AbortError('The task was aborted.');
}
options.signal?.throwIfAborted();

let operation = function_({signal: options.signal});

if (options.timeout) {
operation = pTimeout(Promise.resolve(operation), options.timeout);
operation = pTimeout(Promise.resolve(operation), {milliseconds: options.timeout});
}

if (options.signal) {
Expand Down Expand Up @@ -299,7 +288,7 @@ export default class PQueue<QueueType extends Queue<RunFunction, EnqueueOptionsT
async addAll<TaskResultsType>(
functions: ReadonlyArray<Task<TaskResultsType>>,
options?: {throwOnTimeout: true} & Partial<Exclude<EnqueueOptionsType, 'throwOnTimeout'>>,
): Promise<TaskResultsType[]>
): Promise<TaskResultsType[]>;
async addAll<TaskResultsType>(
functions: ReadonlyArray<Task<TaskResultsType>>,
options?: Partial<EnqueueOptionsType>,
Expand Down Expand Up @@ -430,5 +419,5 @@ export default class PQueue<QueueType extends Queue<RunFunction, EnqueueOptionsT
}
}

// TODO: Rename `DefaultAddOptions` to `QueueAddOptions` in next major version
export {Queue, QueueAddOptions, QueueAddOptions as DefaultAddOptions, Options};
export type {Queue} from './queue.js';
export {type QueueAddOptions, type Options} from './options.js';
18 changes: 9 additions & 9 deletions source/options.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Queue, RunFunction} from './queue.js';
import {type Queue, type RunFunction} from './queue.js';

interface TimeoutOptions {
type TimeoutOptions = {
/**
Per-operation timeout in milliseconds. Operations fulfill once `timeout` elapses if they haven't already.
*/
Expand All @@ -12,9 +12,9 @@ interface TimeoutOptions {
@default false
*/
throwOnTimeout?: boolean;
}
};

export interface Options<QueueType extends Queue<RunFunction, QueueOptions>, QueueOptions extends QueueAddOptions> extends TimeoutOptions {
export type Options<QueueType extends Queue<RunFunction, QueueOptions>, QueueOptions extends QueueAddOptions> = {
/**
Concurrency limit.
Expand Down Expand Up @@ -60,18 +60,18 @@ export interface Options<QueueType extends Queue<RunFunction, QueueOptions>, Que
@default false
*/
readonly carryoverConcurrencyCount?: boolean;
}
} & TimeoutOptions;

export interface QueueAddOptions extends TaskOptions, TimeoutOptions {
export type QueueAddOptions = {
/**
Priority of operation. Operations with greater priority will be scheduled first.
@default 0
*/
readonly priority?: number;
}
} & TaskOptions & TimeoutOptions;

export interface TaskOptions {
export type TaskOptions = {
/**
[`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) for cancellation of the operation. When aborted, it will be removed from the queue and the `queue.add()` call will reject with an `AbortError`. If the operation is already running, the signal will need to be handled by the operation itself.
Expand Down Expand Up @@ -108,4 +108,4 @@ export interface TaskOptions {
```
*/
readonly signal?: AbortSignal;
}
};
8 changes: 4 additions & 4 deletions source/priority-queue.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {Queue, RunFunction} from './queue.js';
import {type Queue, type RunFunction} from './queue.js';
import lowerBound from './lower-bound.js';
import {QueueAddOptions} from './options.js';
import {type QueueAddOptions} from './options.js';

export interface PriorityQueueOptions extends QueueAddOptions {
export type PriorityQueueOptions = {
priority?: number;
}
} & QueueAddOptions;

export default class PriorityQueue implements Queue<RunFunction, PriorityQueueOptions> {
readonly #queue: Array<PriorityQueueOptions & {run: RunFunction}> = [];
Expand Down
4 changes: 2 additions & 2 deletions source/queue.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
export type RunFunction = () => Promise<unknown>;

export interface Queue<Element, Options> {
export type Queue<Element, Options> = {
size: number;
filter: (options: Partial<Options>) => Element[];
dequeue: () => Element | undefined;
enqueue: (run: Element, options?: Partial<Options>) => void;
}
};
6 changes: 3 additions & 3 deletions test/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1103,7 +1103,7 @@ test('should skip an aborted job', async t => {
controller.abort();
// eslint-disable-next-line @typescript-eslint/no-empty-function
await t.throwsAsync(queue.add(() => {}, {signal: controller.signal}), {
instanceOf: AbortError,
instanceOf: DOMException,
});
});

Expand All @@ -1130,7 +1130,7 @@ test('aborting multiple jobs at the same time', async t => {
controller2.abort();
}, 0);

await t.throwsAsync(task1, {instanceOf: AbortError});
await t.throwsAsync(task2, {instanceOf: AbortError});
await t.throwsAsync(task1, {instanceOf: DOMException});
await t.throwsAsync(task2, {instanceOf: DOMException});
t.like(queue, {size: 0, pending: 0});
});

0 comments on commit 68dfe95

Please sign in to comment.