@esm2cjs/p-queue
Version:
Promise queue with concurrency control. This is a fork of sindresorhus/p-queue, but with CommonJS support.
322 lines (314 loc) • 18.3 kB
JavaScript
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _PQueue_instances, _PQueue_carryoverConcurrencyCount, _PQueue_isIntervalIgnored, _PQueue_intervalCount, _PQueue_intervalCap, _PQueue_interval, _PQueue_intervalEnd, _PQueue_intervalId, _PQueue_timeoutId, _PQueue_queue, _PQueue_queueClass, _PQueue_pendingCount, _PQueue_concurrency, _PQueue_isPaused, _PQueue_throwOnTimeout, _PQueue_doesIntervalAllowAnother_get, _PQueue_doesConcurrentAllowAnother_get, _PQueue_next, _PQueue_emitEvents, _PQueue_onResumeInterval, _PQueue_isIntervalPaused_get, _PQueue_tryToStartAnother, _PQueue_initializeIntervalIfNeeded, _PQueue_onInterval, _PQueue_processQueue, _PQueue_onEvent;
import EventEmitter from 'eventemitter3';
import pTimeout, { TimeoutError } from '@esm2cjs/p-timeout';
import PriorityQueue from './priority-queue.js';
const timeoutError = new TimeoutError();
/**
The error thrown by `queue.add()` when a job is aborted before it is run. See `signal`.
*/
export class AbortError extends Error {
}
/**
Promise queue with concurrency control.
*/
export default class PQueue extends EventEmitter {
constructor(options) {
var _a, _b, _c, _d;
super();
_PQueue_instances.add(this);
_PQueue_carryoverConcurrencyCount.set(this, void 0);
_PQueue_isIntervalIgnored.set(this, void 0);
_PQueue_intervalCount.set(this, 0);
_PQueue_intervalCap.set(this, void 0);
_PQueue_interval.set(this, void 0);
_PQueue_intervalEnd.set(this, 0);
_PQueue_intervalId.set(this, void 0);
_PQueue_timeoutId.set(this, void 0);
_PQueue_queue.set(this, void 0);
_PQueue_queueClass.set(this, void 0);
_PQueue_pendingCount.set(this, 0);
// The `!` is needed because of https://github.com/microsoft/TypeScript/issues/32194
_PQueue_concurrency.set(this, void 0);
_PQueue_isPaused.set(this, void 0);
_PQueue_throwOnTimeout.set(this, void 0);
/**
Per-operation timeout in milliseconds. Operations fulfill once `timeout` elapses if they haven't already.
Applies to each future operation.
*/
Object.defineProperty(this, "timeout", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
options = {
carryoverConcurrencyCount: false,
intervalCap: Number.POSITIVE_INFINITY,
interval: 0,
concurrency: Number.POSITIVE_INFINITY,
autoStart: true,
queueClass: PriorityQueue,
...options,
};
if (!(typeof options.intervalCap === 'number' && options.intervalCap >= 1)) {
throw new TypeError(`Expected \`intervalCap\` to be a number from 1 and up, got \`${(_b = (_a = options.intervalCap) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : ''}\` (${typeof options.intervalCap})`);
}
if (options.interval === undefined || !(Number.isFinite(options.interval) && options.interval >= 0)) {
throw new TypeError(`Expected \`interval\` to be a finite number >= 0, got \`${(_d = (_c = options.interval) === null || _c === void 0 ? void 0 : _c.toString()) !== null && _d !== void 0 ? _d : ''}\` (${typeof options.interval})`);
}
__classPrivateFieldSet(this, _PQueue_carryoverConcurrencyCount, options.carryoverConcurrencyCount, "f");
__classPrivateFieldSet(this, _PQueue_isIntervalIgnored, options.intervalCap === Number.POSITIVE_INFINITY || options.interval === 0, "f");
__classPrivateFieldSet(this, _PQueue_intervalCap, options.intervalCap, "f");
__classPrivateFieldSet(this, _PQueue_interval, options.interval, "f");
__classPrivateFieldSet(this, _PQueue_queue, new options.queueClass(), "f");
__classPrivateFieldSet(this, _PQueue_queueClass, options.queueClass, "f");
this.concurrency = options.concurrency;
this.timeout = options.timeout;
__classPrivateFieldSet(this, _PQueue_throwOnTimeout, options.throwOnTimeout === true, "f");
__classPrivateFieldSet(this, _PQueue_isPaused, options.autoStart === false, "f");
}
get concurrency() {
return __classPrivateFieldGet(this, _PQueue_concurrency, "f");
}
set concurrency(newConcurrency) {
if (!(typeof newConcurrency === 'number' && newConcurrency >= 1)) {
throw new TypeError(`Expected \`concurrency\` to be a number from 1 and up, got \`${newConcurrency}\` (${typeof newConcurrency})`);
}
__classPrivateFieldSet(this, _PQueue_concurrency, newConcurrency, "f");
__classPrivateFieldGet(this, _PQueue_instances, "m", _PQueue_processQueue).call(this);
}
/**
Adds a sync or async task to the queue. Always returns a promise.
*/
async add(fn, options = {}) {
return new Promise((resolve, reject) => {
const run = async () => {
var _a;
var _b, _c;
__classPrivateFieldSet(this, _PQueue_pendingCount, (_b = __classPrivateFieldGet(this, _PQueue_pendingCount, "f"), _b++, _b), "f");
__classPrivateFieldSet(this, _PQueue_intervalCount, (_c = __classPrivateFieldGet(this, _PQueue_intervalCount, "f"), _c++, _c), "f");
try {
if ((_a = options.signal) === null || _a === void 0 ? void 0 : _a.aborted) {
// 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.'));
return;
}
const operation = (this.timeout === undefined && options.timeout === undefined) ? fn({ signal: options.signal }) : pTimeout(Promise.resolve(fn({ signal: options.signal })), (options.timeout === undefined ? this.timeout : options.timeout), () => {
if (options.throwOnTimeout === undefined ? __classPrivateFieldGet(this, _PQueue_throwOnTimeout, "f") : options.throwOnTimeout) {
reject(timeoutError);
}
return undefined;
});
const result = await operation;
resolve(result);
this.emit('completed', result);
}
catch (error) {
reject(error);
this.emit('error', error);
}
__classPrivateFieldGet(this, _PQueue_instances, "m", _PQueue_next).call(this);
};
__classPrivateFieldGet(this, _PQueue_queue, "f").enqueue(run, options);
__classPrivateFieldGet(this, _PQueue_instances, "m", _PQueue_tryToStartAnother).call(this);
this.emit('add');
});
}
/**
Same as `.add()`, but accepts an array of sync or async functions.
@returns A promise that resolves when all functions are resolved.
*/
async addAll(functions, options) {
return Promise.all(functions.map(async (function_) => this.add(function_, options)));
}
/**
Start (or resume) executing enqueued tasks within concurrency limit. No need to call this if queue is not paused (via `options.autoStart = false` or by `.pause()` method.)
*/
start() {
if (!__classPrivateFieldGet(this, _PQueue_isPaused, "f")) {
return this;
}
__classPrivateFieldSet(this, _PQueue_isPaused, false, "f");
__classPrivateFieldGet(this, _PQueue_instances, "m", _PQueue_processQueue).call(this);
return this;
}
/**
Put queue execution on hold.
*/
pause() {
__classPrivateFieldSet(this, _PQueue_isPaused, true, "f");
}
/**
Clear the queue.
*/
clear() {
__classPrivateFieldSet(this, _PQueue_queue, new (__classPrivateFieldGet(this, _PQueue_queueClass, "f"))(), "f");
}
/**
Can be called multiple times. Useful if you for example add additional items at a later time.
@returns A promise that settles when the queue becomes empty.
*/
async onEmpty() {
// Instantly resolve if the queue is empty
if (__classPrivateFieldGet(this, _PQueue_queue, "f").size === 0) {
return;
}
await __classPrivateFieldGet(this, _PQueue_instances, "m", _PQueue_onEvent).call(this, 'empty');
}
/**
@returns A promise that settles when the queue size is less than the given limit: `queue.size < limit`.
If you want to avoid having the queue grow beyond a certain size you can `await queue.onSizeLessThan()` before adding a new item.
Note that this only limits the number of items waiting to start. There could still be up to `concurrency` jobs already running that this call does not include in its calculation.
*/
async onSizeLessThan(limit) {
// Instantly resolve if the queue is empty.
if (__classPrivateFieldGet(this, _PQueue_queue, "f").size < limit) {
return;
}
await __classPrivateFieldGet(this, _PQueue_instances, "m", _PQueue_onEvent).call(this, 'next', () => __classPrivateFieldGet(this, _PQueue_queue, "f").size < limit);
}
/**
The difference with `.onEmpty` is that `.onIdle` guarantees that all work from the queue has finished. `.onEmpty` merely signals that the queue is empty, but it could mean that some promises haven't completed yet.
@returns A promise that settles when the queue becomes empty, and all promises have completed; `queue.size === 0 && queue.pending === 0`.
*/
async onIdle() {
// Instantly resolve if none pending and if nothing else is queued
if (__classPrivateFieldGet(this, _PQueue_pendingCount, "f") === 0 && __classPrivateFieldGet(this, _PQueue_queue, "f").size === 0) {
return;
}
await __classPrivateFieldGet(this, _PQueue_instances, "m", _PQueue_onEvent).call(this, 'idle');
}
/**
Size of the queue, the number of queued items waiting to run.
*/
get size() {
return __classPrivateFieldGet(this, _PQueue_queue, "f").size;
}
/**
Size of the queue, filtered by the given options.
For example, this can be used to find the number of items remaining in the queue with a specific priority level.
*/
sizeBy(options) {
// eslint-disable-next-line unicorn/no-array-callback-reference
return __classPrivateFieldGet(this, _PQueue_queue, "f").filter(options).length;
}
/**
Number of running items (no longer in the queue).
*/
get pending() {
return __classPrivateFieldGet(this, _PQueue_pendingCount, "f");
}
/**
Whether the queue is currently paused.
*/
get isPaused() {
return __classPrivateFieldGet(this, _PQueue_isPaused, "f");
}
}
_PQueue_carryoverConcurrencyCount = new WeakMap(), _PQueue_isIntervalIgnored = new WeakMap(), _PQueue_intervalCount = new WeakMap(), _PQueue_intervalCap = new WeakMap(), _PQueue_interval = new WeakMap(), _PQueue_intervalEnd = new WeakMap(), _PQueue_intervalId = new WeakMap(), _PQueue_timeoutId = new WeakMap(), _PQueue_queue = new WeakMap(), _PQueue_queueClass = new WeakMap(), _PQueue_pendingCount = new WeakMap(), _PQueue_concurrency = new WeakMap(), _PQueue_isPaused = new WeakMap(), _PQueue_throwOnTimeout = new WeakMap(), _PQueue_instances = new WeakSet(), _PQueue_doesIntervalAllowAnother_get = function _PQueue_doesIntervalAllowAnother_get() {
return __classPrivateFieldGet(this, _PQueue_isIntervalIgnored, "f") || __classPrivateFieldGet(this, _PQueue_intervalCount, "f") < __classPrivateFieldGet(this, _PQueue_intervalCap, "f");
}, _PQueue_doesConcurrentAllowAnother_get = function _PQueue_doesConcurrentAllowAnother_get() {
return __classPrivateFieldGet(this, _PQueue_pendingCount, "f") < __classPrivateFieldGet(this, _PQueue_concurrency, "f");
}, _PQueue_next = function _PQueue_next() {
var _a;
__classPrivateFieldSet(this, _PQueue_pendingCount, (_a = __classPrivateFieldGet(this, _PQueue_pendingCount, "f"), _a--, _a), "f");
__classPrivateFieldGet(this, _PQueue_instances, "m", _PQueue_tryToStartAnother).call(this);
this.emit('next');
}, _PQueue_emitEvents = function _PQueue_emitEvents() {
this.emit('empty');
if (__classPrivateFieldGet(this, _PQueue_pendingCount, "f") === 0) {
this.emit('idle');
}
}, _PQueue_onResumeInterval = function _PQueue_onResumeInterval() {
__classPrivateFieldGet(this, _PQueue_instances, "m", _PQueue_onInterval).call(this);
__classPrivateFieldGet(this, _PQueue_instances, "m", _PQueue_initializeIntervalIfNeeded).call(this);
__classPrivateFieldSet(this, _PQueue_timeoutId, undefined, "f");
}, _PQueue_isIntervalPaused_get = function _PQueue_isIntervalPaused_get() {
const now = Date.now();
if (__classPrivateFieldGet(this, _PQueue_intervalId, "f") === undefined) {
const delay = __classPrivateFieldGet(this, _PQueue_intervalEnd, "f") - now;
if (delay < 0) {
// Act as the interval was done
// We don't need to resume it here because it will be resumed on line 160
__classPrivateFieldSet(this, _PQueue_intervalCount, (__classPrivateFieldGet(this, _PQueue_carryoverConcurrencyCount, "f")) ? __classPrivateFieldGet(this, _PQueue_pendingCount, "f") : 0, "f");
}
else {
// Act as the interval is pending
if (__classPrivateFieldGet(this, _PQueue_timeoutId, "f") === undefined) {
__classPrivateFieldSet(this, _PQueue_timeoutId, setTimeout(() => {
__classPrivateFieldGet(this, _PQueue_instances, "m", _PQueue_onResumeInterval).call(this);
}, delay), "f");
}
return true;
}
}
return false;
}, _PQueue_tryToStartAnother = function _PQueue_tryToStartAnother() {
if (__classPrivateFieldGet(this, _PQueue_queue, "f").size === 0) {
// We can clear the interval ("pause")
// Because we can redo it later ("resume")
if (__classPrivateFieldGet(this, _PQueue_intervalId, "f")) {
clearInterval(__classPrivateFieldGet(this, _PQueue_intervalId, "f"));
}
__classPrivateFieldSet(this, _PQueue_intervalId, undefined, "f");
__classPrivateFieldGet(this, _PQueue_instances, "m", _PQueue_emitEvents).call(this);
return false;
}
if (!__classPrivateFieldGet(this, _PQueue_isPaused, "f")) {
const canInitializeInterval = !__classPrivateFieldGet(this, _PQueue_instances, "a", _PQueue_isIntervalPaused_get);
if (__classPrivateFieldGet(this, _PQueue_instances, "a", _PQueue_doesIntervalAllowAnother_get) && __classPrivateFieldGet(this, _PQueue_instances, "a", _PQueue_doesConcurrentAllowAnother_get)) {
const job = __classPrivateFieldGet(this, _PQueue_queue, "f").dequeue();
if (!job) {
return false;
}
this.emit('active');
job();
if (canInitializeInterval) {
__classPrivateFieldGet(this, _PQueue_instances, "m", _PQueue_initializeIntervalIfNeeded).call(this);
}
return true;
}
}
return false;
}, _PQueue_initializeIntervalIfNeeded = function _PQueue_initializeIntervalIfNeeded() {
if (__classPrivateFieldGet(this, _PQueue_isIntervalIgnored, "f") || __classPrivateFieldGet(this, _PQueue_intervalId, "f") !== undefined) {
return;
}
__classPrivateFieldSet(this, _PQueue_intervalId, setInterval(() => {
__classPrivateFieldGet(this, _PQueue_instances, "m", _PQueue_onInterval).call(this);
}, __classPrivateFieldGet(this, _PQueue_interval, "f")), "f");
__classPrivateFieldSet(this, _PQueue_intervalEnd, Date.now() + __classPrivateFieldGet(this, _PQueue_interval, "f"), "f");
}, _PQueue_onInterval = function _PQueue_onInterval() {
if (__classPrivateFieldGet(this, _PQueue_intervalCount, "f") === 0 && __classPrivateFieldGet(this, _PQueue_pendingCount, "f") === 0 && __classPrivateFieldGet(this, _PQueue_intervalId, "f")) {
clearInterval(__classPrivateFieldGet(this, _PQueue_intervalId, "f"));
__classPrivateFieldSet(this, _PQueue_intervalId, undefined, "f");
}
__classPrivateFieldSet(this, _PQueue_intervalCount, __classPrivateFieldGet(this, _PQueue_carryoverConcurrencyCount, "f") ? __classPrivateFieldGet(this, _PQueue_pendingCount, "f") : 0, "f");
__classPrivateFieldGet(this, _PQueue_instances, "m", _PQueue_processQueue).call(this);
}, _PQueue_processQueue = function _PQueue_processQueue() {
// eslint-disable-next-line no-empty
while (__classPrivateFieldGet(this, _PQueue_instances, "m", _PQueue_tryToStartAnother).call(this)) { }
}, _PQueue_onEvent = async function _PQueue_onEvent(event, filter) {
return new Promise(resolve => {
const listener = () => {
if (filter && !filter()) {
return;
}
this.off(event, listener);
resolve();
};
this.on(event, listener);
});
};