UNPKG

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
"use strict"; 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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