d4c-queue
Version:
A task queue executes tasks sequentially or concurrently. Wrap an async/promise-returning/sync function as a queue-ready async function for easy reusing. Support passing arguments/getting return value, @synchronized/@concurrent decorator, Node.js/Browser.
390 lines • 29.9 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());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.D4C = exports.synchronized = exports.concurrent = exports.QConcurrency = exports.PreviousTaskError = exports.ErrMsg = void 0;
const Queue_1 = require("./Queue");
var ErrMsg;
(function (ErrMsg) {
ErrMsg["InstanceInvalidTag"] = "instanceInvalidTag: it should be string/symbol/undefined";
ErrMsg["InvalidDecoratorOption"] = "not valid option when using decorators";
ErrMsg["InvalidQueueConcurrency"] = "invalidQueueConcurrency";
ErrMsg["InvalidQueueTag"] = "invalidQueueTag";
ErrMsg["InvalidClassDecoratorParameter"] = "invalidClassDecoratorParameter";
ErrMsg["TwoDecoratorsIncompatible"] = "TwoDecoratorsInCompatible";
ErrMsg["ClassAndMethodDecoratorsIncompatible"] = "ClassAndMethodDecoratorsIncompatible";
ErrMsg["MissingThisDueBindIssue"] = "missingThisDueBindIssue";
ErrMsg["QueueIsFull"] = "QueueIsFull";
})(ErrMsg = exports.ErrMsg || (exports.ErrMsg = {}));
const queueSymbol = Symbol('d4cQueues'); // subQueue system
const concurrentSymbol = Symbol('concurrent'); // record the concurrency of each instance method decorator's tag
const isConcurrentSymbol = Symbol('isConcurrent'); // record isConcurrent of each instance method decorator's tag
const defaultTag = Symbol('D4C');
const DEFAULT_CONCURRENCY = 1;
class PreviousTaskError extends Error {
constructor(message) {
super(message);
this.name = 'PreviousError';
}
}
exports.PreviousTaskError = PreviousTaskError;
function checkIfClassConcurrencyApplyOnSynchronizedMethod(target, usedTag) {
// true means isConcurrent, false means sync, undefined means no static method decorator on this tag
if (target[isConcurrentSymbol][usedTag] === undefined) {
return;
}
else if (target[isConcurrentSymbol][usedTag] === false) {
throw new Error(ErrMsg.ClassAndMethodDecoratorsIncompatible);
}
}
/**
* Class decorator to setup concurrency for queues
* @param queuesParam a array of each queue parameter
*/
function QConcurrency(queuesParam) {
if (!Array.isArray(queuesParam)) {
throw new Error(ErrMsg.InvalidClassDecoratorParameter);
}
/** target is constructor */
return (target) => {
queuesParam.forEach((queueParam) => {
var _a, _b;
if (!queueParam) {
return;
}
const { tag, limit, isStatic } = queueParam;
if (!checkTag(tag) ||
typeof limit !== 'number' ||
(isStatic !== undefined && typeof isStatic !== 'boolean')) {
throw new Error(ErrMsg.InvalidClassDecoratorParameter);
}
const usedTag = tag !== null && tag !== void 0 ? tag : defaultTag;
/** TODO: refactor below as they are use similar code */
if (isStatic) {
// check if at least one static method is using @synchronized/@concurrent
if (!target[queueSymbol]) {
return;
}
checkIfClassConcurrencyApplyOnSynchronizedMethod(target, usedTag);
/** inject concurrency info for each tag in static method case */
if ((_a = target[concurrentSymbol]) === null || _a === void 0 ? void 0 : _a[usedTag]) {
target[concurrentSymbol][usedTag] = limit;
}
}
else {
// check if at least one instance method is using @synchronized/@concurrent
if (target.prototype[queueSymbol] !== null) {
return;
}
checkIfClassConcurrencyApplyOnSynchronizedMethod(target.prototype, usedTag);
/** inject concurrency info for each tag in instance method case */
if ((_b = target.prototype[concurrentSymbol]) === null || _b === void 0 ? void 0 : _b[usedTag]) {
target.prototype[concurrentSymbol][usedTag] = limit;
}
}
});
};
}
exports.QConcurrency = QConcurrency;
function checkTag(tag) {
if (tag === undefined || typeof tag === 'string' || typeof tag === 'symbol') {
return true;
}
return false;
}
function checkIfTwoDecoratorsHaveSameConcurrentValue(target, tag, isConcurrent) {
// init
if (!target[isConcurrentSymbol]) {
target[isConcurrentSymbol] = {};
}
// check if two decorators for same queue have same isConcurrency value
if (target[isConcurrentSymbol][tag] === undefined) {
target[isConcurrentSymbol][tag] = isConcurrent;
}
else if (target[isConcurrentSymbol][tag] !== isConcurrent) {
throw new Error(ErrMsg.TwoDecoratorsIncompatible);
}
/** set default concurrency is infinity for @concurrent on instance/static methods*/
if (isConcurrent) {
if (!target[concurrentSymbol]) {
target[concurrentSymbol] = {};
}
target[concurrentSymbol][tag] = Infinity;
}
}
function injectQueue(constructorOrPrototype, tag, isConcurrent) {
if (constructorOrPrototype.prototype) {
// constructor, means static method
if (!constructorOrPrototype[queueSymbol]) {
constructorOrPrototype[queueSymbol] = new Map();
}
}
else {
// prototype, means instance method
if (constructorOrPrototype[queueSymbol] !== null) {
constructorOrPrototype[queueSymbol] = null;
}
}
checkIfTwoDecoratorsHaveSameConcurrentValue(constructorOrPrototype, tag, isConcurrent);
}
/** if class has a static member call inheritPreErr, even no using parentheses,
* targetOrOption will have targetOrOption property but its type is function */
function checkIfDecoratorOptionObject(obj) {
/** still count valid argument, e.g. @synchronized(null) */
if (obj === undefined || obj === null) {
return true;
}
/**
* hasOwnProperty should be false since it is a literal object
*/
if (typeof obj === 'object' &&
//eslint-disable-next-line
!obj.hasOwnProperty('constructor') &&
(typeof obj.inheritPreErr === 'boolean' ||
obj.inheritPreErr === undefined) &&
(typeof obj.noBlockCurr === 'boolean' || obj.noBlockCurr === undefined) &&
(typeof obj.dropWhenReachLimit === 'boolean' ||
obj.dropWhenReachLimit === undefined) &&
checkTag(obj.tag)) {
return true;
}
return false;
}
function concurrent(targetOrOption, propertyKey, descriptor) {
return _methodDecorator(targetOrOption, propertyKey, descriptor, true);
}
exports.concurrent = concurrent;
function synchronized(targetOrOption, propertyKey, descriptor) {
return _methodDecorator(targetOrOption, propertyKey, descriptor, false);
}
exports.synchronized = synchronized;
function _methodDecorator(targetOrOption, propertyKey, descriptor, isConcurrent) {
if (checkIfDecoratorOptionObject(targetOrOption)) {
/** parentheses case containing option (=targetOrOption) */
return function (target, propertyKey, descriptor) {
var _a;
injectQueue(target, (_a = targetOrOption === null || targetOrOption === void 0 ? void 0 : targetOrOption.tag) !== null && _a !== void 0 ? _a : defaultTag, isConcurrent);
const originalMethod = descriptor.value;
const newFunc = _q(null, originalMethod, targetOrOption);
descriptor.value = newFunc;
};
}
else {
/** no parentheses case */
const type = typeof targetOrOption;
/**
* static method decorator case: target type is constructor function. use target.prototype
* method decorator case: target is a prototype object, not literally object. use target
*/
if ((type === 'function' || targetOrOption.hasOwnProperty('constructor')) && // eslint-disable-line
typeof propertyKey === 'string' &&
typeof descriptor === 'object' &&
typeof descriptor.value === 'function') {
injectQueue(targetOrOption, defaultTag, isConcurrent);
const originalMethod = descriptor.value;
const newFunc = _q(null, originalMethod, {});
descriptor.value = newFunc;
}
else {
throw new Error(ErrMsg.InvalidDecoratorOption);
}
}
}
function _q(d4cObj, func, option) {
return function (...args) {
var _a, _b, _c, _d;
return __awaiter(this, void 0, void 0, function* () {
/** Detect tag */
let tag;
if ((option === null || option === void 0 ? void 0 : option.tag) !== undefined) {
tag = option.tag;
}
else {
tag = defaultTag;
}
let decoratorConcurrencyLimit;
/** Assign queues */
let taskQueue;
let currTaskQueues;
if (d4cObj) {
/** D4C instance case */
currTaskQueues = d4cObj.queues;
}
else if (this && (this[queueSymbol] || this[queueSymbol] === null)) {
if (this[queueSymbol] === null) {
/** instance method decorator first time case, using injected queues in user defined objects*/
this[queueSymbol] = new Map();
}
currTaskQueues = this[queueSymbol];
decoratorConcurrencyLimit = (_a = this[concurrentSymbol]) === null || _a === void 0 ? void 0 : _a[tag];
}
else {
throw new Error(ErrMsg.MissingThisDueBindIssue);
}
/** Get sub-queue */
taskQueue = currTaskQueues.get(tag);
if (!taskQueue) {
taskQueue = {
queue: new Queue_1.Queue(),
isRunning: false,
runningTask: 0,
/** D4C instance usage ?? (Decorator usage - specified limit ?? decorator - unspecified case) */
concurrency: (_c = (_b = d4cObj === null || d4cObj === void 0 ? void 0 : d4cObj.defaultConcurrency) !== null && _b !== void 0 ? _b : decoratorConcurrencyLimit) !== null && _c !== void 0 ? _c : DEFAULT_CONCURRENCY,
};
currTaskQueues.set(tag, taskQueue);
}
/** Detect if the queue is running or not, use promise to wait it if it is running */
let result;
let err;
let task;
if (taskQueue.runningTask === taskQueue.concurrency) {
if (!(option === null || option === void 0 ? void 0 : option.dropWhenReachLimit)) {
const promise = new Promise(function (resolve) {
task = {
unlock: resolve,
preError: null,
inheritPreErr: option === null || option === void 0 ? void 0 : option.inheritPreErr,
};
});
taskQueue.queue.push(task);
yield promise;
taskQueue.runningTask += 1;
}
else {
// drop this time, throttle mechanism
throw new Error(ErrMsg.QueueIsFull);
}
}
else if (option === null || option === void 0 ? void 0 : option.noBlockCurr) {
taskQueue.runningTask += 1;
yield Promise.resolve();
}
else {
taskQueue.runningTask += 1;
}
/** Run the task */
if (task === null || task === void 0 ? void 0 : task.preError) {
err = new PreviousTaskError((_d = task.preError.message) !== null && _d !== void 0 ? _d : task.preError);
}
else {
try {
/** this will be constructor function for static method case */
const value = func.apply(this, args);
/** Detect if it is a async/promise function or not */
if (value && typeof value.then === 'function') {
result = yield value;
}
else {
result = value;
}
}
catch (error) {
err = error;
}
}
taskQueue.runningTask -= 1;
/** After the task is executed, check the following tasks */
if (taskQueue.queue.length > 0) {
const nextTask = taskQueue.queue.shift();
/** Pass error to next task */
if (err && nextTask.inheritPreErr) {
nextTask.preError = err;
}
nextTask.unlock();
}
if (err) {
throw err;
}
return result;
});
};
}
class D4C {
/**
* Default concurrency is 1. Omitting tag means it is for default queue.
* If you specify concurrency limit for some tag queue,
* this instance will not use that tag queue by default.
*/
constructor(queuesParam) {
this.defaultConcurrency = DEFAULT_CONCURRENCY;
this.queues = new Map();
if (Array.isArray(queuesParam)) {
queuesParam.forEach((option) => {
var _a;
if (((_a = option === null || option === void 0 ? void 0 : option.concurrency) === null || _a === void 0 ? void 0 : _a.limit) > 0) {
this._setConcurrency(option.concurrency);
}
});
}
}
/**
* @param option tag is optional for specific queue. omitting is for default queue
* @param option.limit is limit of concurrency and should be >= 1
*/
setConcurrency(queuesParam) {
if (Array.isArray(queuesParam)) {
queuesParam.forEach((option) => {
this._setConcurrency(option);
});
}
}
_setConcurrency(concurrency) {
if ((concurrency === null || concurrency === void 0 ? void 0 : concurrency.limit) === undefined ||
typeof concurrency.limit !== 'number') {
throw new Error(ErrMsg.InvalidQueueConcurrency);
}
const { tag, limit } = concurrency;
if (limit < 1) {
throw new Error(ErrMsg.InvalidQueueConcurrency);
}
if (!checkTag(tag)) {
throw new Error(ErrMsg.InvalidQueueTag);
}
// TODO: refactor this, _q has similar code */
let usedTag;
if (tag !== undefined) {
usedTag = tag;
}
else {
usedTag = defaultTag;
}
// TODO: refactor, other places have similar code
let taskQueue = this.queues.get(usedTag);
if (!taskQueue) {
taskQueue = {
queue: new Queue_1.Queue(),
isRunning: false,
runningTask: 0,
concurrency: limit,
};
}
else {
taskQueue.concurrency = limit;
}
this.queues.set(usedTag, taskQueue);
}
/** It wraps original function for queue ready and executes it*/
apply(func, option) {
const resp = this.wrap(func, option).apply(null, option === null || option === void 0 ? void 0 : option.args);
return resp;
}
/** It wraps original function for queue ready */
wrap(func, option) {
if (!option || checkTag(option.tag)) {
return _q({
queues: this.queues,
defaultConcurrency: this.defaultConcurrency,
}, func, option);
}
throw new Error(ErrMsg.InstanceInvalidTag);
}
}
exports.D4C = D4C;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRDRDLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2xpYi9ENEMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7O0FBQUEsbUNBQStCO0FBaUMvQixJQUFZLE1BVVg7QUFWRCxXQUFZLE1BQU07SUFDaEIseUZBQStFLENBQUE7SUFDL0UsMkVBQWlFLENBQUE7SUFDakUsNkRBQW1ELENBQUE7SUFDbkQsNkNBQW1DLENBQUE7SUFDbkMsMkVBQWlFLENBQUE7SUFDakUsaUVBQXVELENBQUE7SUFDdkQsdUZBQTZFLENBQUE7SUFDN0UsNkRBQW1ELENBQUE7SUFDbkQscUNBQTJCLENBQUE7QUFDN0IsQ0FBQyxFQVZXLE1BQU0sR0FBTixjQUFNLEtBQU4sY0FBTSxRQVVqQjtBQUVELE1BQU0sV0FBVyxHQUFHLE1BQU0sQ0FBQyxXQUFXLENBQUMsQ0FBQSxDQUFDLGtCQUFrQjtBQUMxRCxNQUFNLGdCQUFnQixHQUFHLE1BQU0sQ0FBQyxZQUFZLENBQUMsQ0FBQSxDQUFDLGlFQUFpRTtBQUMvRyxNQUFNLGtCQUFrQixHQUFHLE1BQU0sQ0FBQyxjQUFjLENBQUMsQ0FBQSxDQUFDLDhEQUE4RDtBQUVoSCxNQUFNLFVBQVUsR0FBRyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUE7QUFFaEMsTUFBTSxtQkFBbUIsR0FBRyxDQUFDLENBQUE7QUFFN0IsTUFBYSxpQkFBa0IsU0FBUSxLQUFLO0lBQzFDLFlBQVksT0FBTztRQUNqQixLQUFLLENBQUMsT0FBTyxDQUFDLENBQUE7UUFDZCxJQUFJLENBQUMsSUFBSSxHQUFHLGVBQWUsQ0FBQTtJQUM3QixDQUFDO0NBQ0Y7QUFMRCw4Q0FLQztBQUVELFNBQVMsZ0RBQWdELENBQ3ZELE1BQU0sRUFDTixPQUF3QjtJQUV4QixvR0FBb0c7SUFDcEcsSUFBSSxNQUFNLENBQUMsa0JBQWtCLENBQUMsQ0FBQyxPQUFPLENBQUMsS0FBSyxTQUFTLEVBQUU7UUFDckQsT0FBTTtLQUNQO1NBQU0s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
;