2023-01-18 16:45:19 +00:00
|
|
|
import * as plugins from './lik.plugins.js';
|
|
|
|
|
|
|
|
interface IExecutionSlot<T> {
|
|
|
|
executionDeferred: plugins.smartpromise.Deferred<T>;
|
|
|
|
funcToExecute: () => Promise<T>;
|
|
|
|
timeout?: number;
|
|
|
|
mode: 'exclusive' | 'nonexclusive';
|
|
|
|
}
|
2023-01-18 11:18:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* allows for avoiding race condition
|
|
|
|
*/
|
|
|
|
export class AsyncExecutionStack {
|
2023-01-18 16:45:19 +00:00
|
|
|
private executionSlots: IExecutionSlot<any>[] = [];
|
|
|
|
public async getExclusiveExecutionSlot<T = any>(funcArg: () => Promise<T>, timeoutArg?: number) {
|
|
|
|
const executionDeferred = plugins.smartpromise.defer<T>();
|
|
|
|
const executionSlot: IExecutionSlot<T> = {
|
|
|
|
funcToExecute: funcArg,
|
|
|
|
executionDeferred,
|
|
|
|
timeout: timeoutArg,
|
|
|
|
mode: 'exclusive',
|
|
|
|
};
|
|
|
|
this.executionSlots.push(executionSlot);
|
|
|
|
this.processExecutionSlots();
|
|
|
|
return executionDeferred.promise;
|
|
|
|
}
|
|
|
|
public async getNonExclusiveExecutionSlot<T = any>(
|
|
|
|
funcArg: () => Promise<T>,
|
|
|
|
timeoutArg?: number
|
|
|
|
) {
|
|
|
|
const executionDeferred = plugins.smartpromise.defer<T>();
|
|
|
|
const executionSlot: IExecutionSlot<T> = {
|
|
|
|
funcToExecute: funcArg,
|
|
|
|
executionDeferred,
|
|
|
|
timeout: timeoutArg,
|
|
|
|
mode: 'nonexclusive',
|
|
|
|
};
|
|
|
|
this.executionSlots.push(executionSlot);
|
|
|
|
this.processExecutionSlots();
|
|
|
|
return executionDeferred.promise;
|
|
|
|
}
|
|
|
|
|
|
|
|
private currentlyExecutingDeferred: plugins.smartpromise.Deferred<any>;
|
|
|
|
private async processExecutionSlots() {
|
|
|
|
if (this.currentlyExecutingDeferred) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.currentlyExecutingDeferred = plugins.smartpromise.defer();
|
|
|
|
let nonExclusiveRunningSlots: IExecutionSlot<any>[] = [];
|
|
|
|
const checkNonExclusiveRunningSlots = async (cleanArg = false) => {
|
|
|
|
if (nonExclusiveRunningSlots.length > 100 || cleanArg) {
|
|
|
|
await Promise.all(nonExclusiveRunningSlots.map(nonExclusiveRunningSlotArg => nonExclusiveRunningSlotArg.executionDeferred.promise));
|
|
|
|
nonExclusiveRunningSlots = [];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
while (this.executionSlots.length > 0) {
|
|
|
|
const nextExecutionSlot = this.executionSlots.shift();
|
|
|
|
const runNextExecution = async () => {
|
|
|
|
if (nextExecutionSlot.timeout) {
|
|
|
|
const result = await Promise.race([
|
|
|
|
nextExecutionSlot.funcToExecute(),
|
|
|
|
plugins.smartdelay.delayFor(nextExecutionSlot.timeout),
|
|
|
|
]);
|
|
|
|
nextExecutionSlot.executionDeferred.resolve(result);
|
2023-01-18 11:18:47 +00:00
|
|
|
} else {
|
2023-01-18 16:45:19 +00:00
|
|
|
nextExecutionSlot.executionDeferred.resolve(await nextExecutionSlot.funcToExecute());
|
2023-01-18 11:18:47 +00:00
|
|
|
}
|
2023-01-18 16:45:19 +00:00
|
|
|
};
|
|
|
|
if (nextExecutionSlot.mode === 'exclusive') {
|
|
|
|
await checkNonExclusiveRunningSlots(true);
|
|
|
|
await runNextExecution();
|
|
|
|
} else {
|
|
|
|
nonExclusiveRunningSlots.push(nextExecutionSlot);
|
|
|
|
await checkNonExclusiveRunningSlots(false);
|
|
|
|
runNextExecution();
|
2023-01-18 11:18:47 +00:00
|
|
|
}
|
|
|
|
}
|
2023-01-18 16:45:19 +00:00
|
|
|
this.currentlyExecutingDeferred.resolve();
|
|
|
|
this.currentlyExecutingDeferred = null;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|