UNPKG

@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
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); }); };