2020-11-05 18:58:23 +02:00
|
|
|
import shim from './shim';
|
2020-10-09 19:35:46 +02:00
|
|
|
|
2020-03-10 01:24:57 +02:00
|
|
|
export interface QueueItemAction {
|
2020-11-12 21:29:22 +02:00
|
|
|
(): void;
|
2020-03-10 01:24:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface QueueItem {
|
2020-11-12 21:29:22 +02:00
|
|
|
action: QueueItemAction;
|
2024-04-05 13:16:49 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
2020-11-12 21:29:22 +02:00
|
|
|
context: any;
|
2020-03-10 01:24:57 +02:00
|
|
|
}
|
|
|
|
|
2020-06-13 17:20:18 +02:00
|
|
|
export enum IntervalType {
|
|
|
|
Debounce = 1,
|
|
|
|
Fixed = 2,
|
|
|
|
}
|
|
|
|
|
2020-05-30 18:49:29 +02:00
|
|
|
// The AsyncActionQueue can be used to debounce asynchronous actions, to make sure
|
|
|
|
// they run in the right order, and also to ensure that if multiple actions are emitted
|
2020-06-02 23:35:41 +02:00
|
|
|
// only the last one is executed. This is particularly useful to save data in the background.
|
2020-05-30 18:49:29 +02:00
|
|
|
// Each queue should be associated with a specific entity (a note, resource, etc.)
|
2020-03-10 01:24:57 +02:00
|
|
|
export default class AsyncActionQueue {
|
|
|
|
|
2021-01-07 18:30:53 +02:00
|
|
|
private queue_: QueueItem[] = [];
|
|
|
|
private interval_: number;
|
|
|
|
private intervalType_: number;
|
2024-04-05 13:16:49 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
2021-01-07 18:30:53 +02:00
|
|
|
private scheduleProcessingIID_: any = null;
|
|
|
|
private processing_ = false;
|
|
|
|
|
2023-06-30 10:11:26 +02:00
|
|
|
public constructor(interval = 100, intervalType: IntervalType = IntervalType.Debounce) {
|
2020-03-10 01:24:57 +02:00
|
|
|
this.interval_ = interval;
|
2020-06-13 17:20:18 +02:00
|
|
|
this.intervalType_ = intervalType;
|
2020-03-10 01:24:57 +02:00
|
|
|
}
|
|
|
|
|
2024-04-05 13:16:49 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
2021-01-07 18:30:53 +02:00
|
|
|
public push(action: QueueItemAction, context: any = null) {
|
2020-03-10 01:24:57 +02:00
|
|
|
this.queue_.push({
|
|
|
|
action: action,
|
|
|
|
context: context,
|
|
|
|
});
|
|
|
|
this.scheduleProcessing();
|
|
|
|
}
|
|
|
|
|
2021-01-07 18:30:53 +02:00
|
|
|
public get isEmpty(): boolean {
|
|
|
|
return !this.queue_.length;
|
2020-03-10 01:24:57 +02:00
|
|
|
}
|
|
|
|
|
2020-11-12 21:13:28 +02:00
|
|
|
private scheduleProcessing(interval: number = null) {
|
2020-03-10 01:24:57 +02:00
|
|
|
if (interval === null) interval = this.interval_;
|
|
|
|
|
|
|
|
if (this.scheduleProcessingIID_) {
|
2020-06-13 17:20:18 +02:00
|
|
|
if (this.intervalType_ === IntervalType.Fixed) return;
|
2020-10-09 19:35:46 +02:00
|
|
|
shim.clearTimeout(this.scheduleProcessingIID_);
|
2020-03-10 01:24:57 +02:00
|
|
|
}
|
|
|
|
|
2020-10-09 19:35:46 +02:00
|
|
|
this.scheduleProcessingIID_ = shim.setTimeout(() => {
|
2020-03-10 01:24:57 +02:00
|
|
|
this.scheduleProcessingIID_ = null;
|
2020-11-25 16:40:25 +02:00
|
|
|
void this.processQueue();
|
2020-03-10 01:24:57 +02:00
|
|
|
}, interval);
|
|
|
|
}
|
|
|
|
|
|
|
|
private async processQueue() {
|
|
|
|
if (this.processing_) {
|
|
|
|
this.scheduleProcessing();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.processing_ = true;
|
|
|
|
|
|
|
|
const itemCount = this.queue_.length;
|
|
|
|
|
|
|
|
if (itemCount) {
|
|
|
|
const item = this.queue_[itemCount - 1];
|
|
|
|
await item.action();
|
|
|
|
this.queue_.splice(0, itemCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.processing_ = false;
|
|
|
|
}
|
|
|
|
|
2021-01-07 18:30:53 +02:00
|
|
|
public async reset() {
|
2020-06-13 17:20:18 +02:00
|
|
|
if (this.scheduleProcessingIID_) {
|
2020-10-09 19:35:46 +02:00
|
|
|
shim.clearTimeout(this.scheduleProcessingIID_);
|
2020-06-13 17:20:18 +02:00
|
|
|
this.scheduleProcessingIID_ = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.queue_ = [];
|
|
|
|
return this.waitForAllDone();
|
|
|
|
}
|
|
|
|
|
2020-06-10 01:30:32 +02:00
|
|
|
// Currently waitForAllDone() already finishes all the actions
|
|
|
|
// as quickly as possible so we can make it an alias.
|
2021-01-07 18:30:53 +02:00
|
|
|
public async processAllNow() {
|
2020-06-10 01:30:32 +02:00
|
|
|
return this.waitForAllDone();
|
|
|
|
}
|
|
|
|
|
2021-01-07 18:30:53 +02:00
|
|
|
public async waitForAllDone() {
|
2020-05-30 18:49:29 +02:00
|
|
|
if (!this.queue_.length) return Promise.resolve();
|
|
|
|
|
2020-03-10 01:24:57 +02:00
|
|
|
this.scheduleProcessing(1);
|
|
|
|
|
|
|
|
return new Promise((resolve) => {
|
2020-10-09 19:35:46 +02:00
|
|
|
const iid = shim.setInterval(() => {
|
2020-03-10 01:24:57 +02:00
|
|
|
if (this.processing_) return;
|
|
|
|
|
|
|
|
if (!this.queue_.length) {
|
2020-10-09 19:35:46 +02:00
|
|
|
shim.clearInterval(iid);
|
2021-03-15 18:33:42 +02:00
|
|
|
resolve(null);
|
2020-03-10 01:24:57 +02:00
|
|
|
}
|
|
|
|
}, 100);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|