easy-action-queue
Version:
A lightweight library to queue and manage actions with configurable concurrency, supporting synchronous, asynchronous, and observable actions.
141 lines • 5.5 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { BehaviorSubject, Observable, distinctUntilChanged, firstValueFrom, } from "rxjs";
export default class EasyActionQueue {
constructor(concurrency = 1) {
this.concurrency = concurrency;
this.queue = [];
this.runningActions = new Set();
this.idleSub = new BehaviorSubject(false);
this.paused = false;
if (!Number.isInteger(concurrency) || concurrency < 1) {
console.warn("Concurrency must be a positive integer, using 1");
this.concurrency = 1;
}
}
// If we want idle state observable
get idleObs() {
return this.idleSub.asObservable().pipe(distinctUntilChanged());
}
// Method to get the current queue size
getQueueSize() {
return this.queue.length;
}
// Method to update the concurrency during runtime
updateConcurrency(newConcurrency) {
if (!Number.isInteger(newConcurrency) || newConcurrency < 1) {
throw new Error("Concurrency must be a positive integer");
}
this.concurrency = newConcurrency;
this.processQueue();
}
// Method to pause the queue
pause() {
this.paused = true;
}
// Method to resume the queue
resume() {
if (this.paused) {
this.paused = false;
this.processQueue();
}
}
/**
* T is type for final value, R is type of return value from the callback
* action callback will be enqueued and when it gets triggered, the promise will resolve with the result
* if the callback returns a string, it will be resolved in promise
* if it is async operation, the result will be resolved in promise
*/
enqueue(action) {
return __awaiter(this, void 0, void 0, function* () {
this.idleSub.next(false);
return new Promise((resolve, reject) => {
this.queue.push({ action, resolve, reject });
this.processQueue();
});
});
}
clearQueue() {
const rejectAndResolve = (item) => {
item.cleared = true;
item.reject("EasyActionQueue has been cleared!");
};
this.runningActions.forEach(rejectAndResolve);
this.runningActions.clear();
while (this.queue.length > 0) {
const item = this.queue.shift();
if (item) {
rejectAndResolve(item);
}
}
this.idleSub.next(true);
}
processQueue() {
return __awaiter(this, void 0, void 0, function* () {
if (this.paused)
return;
while (this.runningActions.size < this.concurrency &&
this.queue.length > 0) {
const item = this.queue.shift();
this.runningActions.add(item);
try {
const response = item.action();
if (response instanceof Promise) {
response
.then((result) => this.handleResponse(result, item))
.catch((error) => this.handleRejection(error || "Unknown error", item));
}
else if (response instanceof Observable) {
firstValueFrom(response)
.then((result) => this.handleResponse(result, item))
.catch((error) => this.handleRejection(error || "Unknown error", item));
}
else {
this.handleResponse(response, item);
}
}
catch (error) {
this.handleRejection(error || "Unknown error", item);
}
}
});
}
handleResponse(result, item) {
return __awaiter(this, void 0, void 0, function* () {
this.handleCompletion(item, result);
});
}
handleRejection(error, item) {
return __awaiter(this, void 0, void 0, function* () {
this.handleCompletion(item, undefined, error);
});
}
handleCompletion(item, result, error) {
return __awaiter(this, void 0, void 0, function* () {
this.runningActions.delete(item);
if (item.cleared)
return;
if (error) {
item.reject(error);
}
else {
item.resolve(result);
}
const lastIdleState = yield firstValueFrom(this.idleObs);
if (this.runningActions.size === 0 &&
this.queue.length === 0 &&
!lastIdleState) {
this.idleSub.next(true);
}
this.processQueue();
});
}
}
//# sourceMappingURL=ActionQueue.js.map