import * as plugins from './classes.plugins.js'; interface IExecutionSlot { executionDeferred: plugins.smartpromise.Deferred; funcToExecute: () => Promise; timeout?: number; mode: 'exclusive' | 'nonexclusive'; } export class AsyncExecutionStack { private executionSlots: IExecutionSlot[] = []; private isProcessing = false; public async getExclusiveExecutionSlot( funcArg: () => Promise, timeoutArg?: number ): Promise { const executionDeferred = plugins.smartpromise.defer(); const executionSlot: IExecutionSlot = { funcToExecute: funcArg, executionDeferred, timeout: timeoutArg, mode: 'exclusive', }; this.executionSlots.push(executionSlot); this.processExecutionSlots(); return executionDeferred.promise; } public async getNonExclusiveExecutionSlot( funcArg: () => Promise, timeoutArg?: number ): Promise { const executionDeferred = plugins.smartpromise.defer(); const executionSlot: IExecutionSlot = { funcToExecute: funcArg, executionDeferred, timeout: timeoutArg, mode: 'nonexclusive', }; this.executionSlots.push(executionSlot); this.processExecutionSlots(); return executionDeferred.promise; } private 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: IExecutionSlot[] = []; while (this.executionSlots.length > 0 && this.executionSlots[0].mode === 'nonexclusive') { nonExclusiveSlots.push(this.executionSlots.shift()!); } await this.executeNonExclusiveSlots(nonExclusiveSlots); } } this.isProcessing = false; } private async executeExclusiveSlot(slot: IExecutionSlot) { 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); } } private async executeNonExclusiveSlots(slots: IExecutionSlot[]) { const promises = slots.map(async (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); } }); await Promise.all(promises); } }