UNPKG

@push.rocks/lik

Version:

Provides a collection of lightweight helpers and utilities for Node.js projects.

152 lines 11.4 kB
import * as plugins from './classes.plugins.js'; export class AsyncExecutionStack { constructor() { this.executionSlots = []; this.isProcessing = false; /** Maximum concurrent non-exclusive tasks (Infinity = unlimited) */ this.nonExclusiveMaxConcurrency = Infinity; /** Currently running non-exclusive task count */ this.nonExclusiveCurrentCount = 0; /** Queue of resolvers waiting for a non-exclusive slot */ this.nonExclusivePendingQueue = []; } async getExclusiveExecutionSlot(funcArg, timeoutArg) { const executionDeferred = plugins.smartpromise.defer(); const executionSlot = { funcToExecute: funcArg, executionDeferred, timeout: timeoutArg, mode: 'exclusive', }; this.executionSlots.push(executionSlot); this.processExecutionSlots(); return executionDeferred.promise; } async getNonExclusiveExecutionSlot(funcArg, timeoutArg) { const executionDeferred = plugins.smartpromise.defer(); const executionSlot = { funcToExecute: funcArg, executionDeferred, timeout: timeoutArg, mode: 'nonexclusive', }; this.executionSlots.push(executionSlot); this.processExecutionSlots(); return executionDeferred.promise; } /** * Set the maximum number of concurrent non-exclusive tasks. * @param concurrency minimum 1 (Infinity means unlimited) */ setNonExclusiveMaxConcurrency(concurrency) { if (!Number.isFinite(concurrency) || concurrency < 1) { throw new Error('nonExclusiveMaxConcurrency must be a finite number >= 1'); } this.nonExclusiveMaxConcurrency = concurrency; } /** Get the configured max concurrency for non-exclusive tasks */ getNonExclusiveMaxConcurrency() { return this.nonExclusiveMaxConcurrency; } /** Number of non-exclusive tasks currently running */ getActiveNonExclusiveCount() { return this.nonExclusiveCurrentCount; } /** Number of non-exclusive tasks waiting for a free slot */ getPendingNonExclusiveCount() { return this.nonExclusivePendingQueue.length; } async processExecutionSlots() { if (this.isProcessing) { return; } this.isProcessing = true; while (this.executionSlots.length > 0) { const currentSlot = this.executionSlots[0]; if (currentSlot.mode === 'exclusive') { await this.executeExclusiveSlot(currentSlot); this.executionSlots.shift(); } else { // Gather all non-exclusive slots at the front of the queue const nonExclusiveSlots = []; while (this.executionSlots.length > 0 && this.executionSlots[0].mode === 'nonexclusive') { nonExclusiveSlots.push(this.executionSlots.shift()); } await this.executeNonExclusiveSlots(nonExclusiveSlots); } } this.isProcessing = false; } async executeExclusiveSlot(slot) { try { if (slot.timeout) { const result = await Promise.race([ slot.funcToExecute(), plugins.smartdelay.delayFor(slot.timeout).then(() => { throw new Error('Timeout reached'); }), ]); slot.executionDeferred.resolve(result); } else { const result = await slot.funcToExecute(); slot.executionDeferred.resolve(result); } } catch (error) { slot.executionDeferred.reject(error); } } async executeNonExclusiveSlots(slots) { const promises = slots.map(async (slot) => { // wait for an available non-exclusive slot await this.waitForNonExclusiveSlot(); try { // execute with optional timeout if (slot.timeout) { const result = await Promise.race([ slot.funcToExecute(), plugins.smartdelay.delayFor(slot.timeout).then(() => { throw new Error('Timeout reached'); }), ]); slot.executionDeferred.resolve(result); } else { const result = await slot.funcToExecute(); slot.executionDeferred.resolve(result); } } catch (error) { slot.executionDeferred.reject(error); } finally { this.releaseNonExclusiveSlot(); } }); await Promise.all(promises); } /** * Wait until a non-exclusive slot is available (respects max concurrency). */ waitForNonExclusiveSlot() { if (this.nonExclusiveCurrentCount < this.nonExclusiveMaxConcurrency) { this.nonExclusiveCurrentCount++; return Promise.resolve(); } return new Promise((resolve) => { this.nonExclusivePendingQueue.push(() => { this.nonExclusiveCurrentCount++; resolve(); }); }); } /** Release a non-exclusive slot and wake the next waiter, if any. */ releaseNonExclusiveSlot() { this.nonExclusiveCurrentCount--; const next = this.nonExclusivePendingQueue.shift(); if (next) { next(); } } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5hc3luY2V4ZWN1dGlvbnN0YWNrLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY2xhc3Nlcy5hc3luY2V4ZWN1dGlvbnN0YWNrLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sc0JBQXNCLENBQUM7QUFTaEQsTUFBTSxPQUFPLG1CQUFtQjtJQUFoQztRQUNVLG1CQUFjLEdBQTBCLEVBQUUsQ0FBQztRQUMzQyxpQkFBWSxHQUFHLEtBQUssQ0FBQztRQUM3QixvRUFBb0U7UUFDNUQsK0JBQTBCLEdBQVcsUUFBUSxDQUFDO1FBQ3RELGlEQUFpRDtRQUN6Qyw2QkFBd0IsR0FBVyxDQUFDLENBQUM7UUFDN0MsMERBQTBEO1FBQ2xELDZCQUF3QixHQUFzQixFQUFFLENBQUM7SUFpSjNELENBQUM7SUEvSVEsS0FBSyxDQUFDLHlCQUF5QixDQUNwQyxPQUF5QixFQUN6QixVQUFtQjtRQUVuQixNQUFNLGlCQUFpQixHQUFHLE9BQU8sQ0FBQyxZQUFZLENBQUMsS0FBSyxFQUFLLENBQUM7UUFDMUQsTUFBTSxhQUFhLEdBQXNCO1lBQ3ZDLGFBQWEsRUFBRSxPQUFPO1lBQ3RCLGlCQUFpQjtZQUNqQixPQUFPLEVBQUUsVUFBVTtZQUNuQixJQUFJLEVBQUUsV0FBVztTQUNsQixDQUFDO1FBQ0YsSUFBSSxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLENBQUM7UUFDeEMsSUFBSSxDQUFDLHFCQUFxQixFQUFFLENBQUM7UUFDN0IsT0FBTyxpQkFBaUIsQ0FBQyxPQUFPLENBQUM7SUFDbkMsQ0FBQztJQUVNLEtBQUssQ0FBQyw0QkFBNEIsQ0FDdkMsT0FBeUIsRUFDekIsVUFBbUI7UUFFbkIsTUFBTSxpQkFBaUIsR0FBRyxPQUFPLENBQUMsWUFBWSxDQUFDLEtBQUssRUFBSyxDQUFDO1FBQzFELE1BQU0sYUFBYSxHQUFzQjtZQUN2QyxhQUFhLEVBQUUsT0FBTztZQUN0QixpQkFBaUI7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