grouped-queue
Version:
In memory queue system prioritizing tasks
161 lines (133 loc) • 3.83 kB
JavaScript
"use strict";
var util = require("util");
var events = require("events");
var SubQueue = require("./subqueue");
module.exports = Queue;
/**
* Queue constructor
* @param {String[]} [subQueue] The order of the sub-queues. First one will be runned first.
*/
function Queue(subQueues, runOnAdd = true) {
subQueues = subQueues || [];
if (!subQueues.includes("default")) {
subQueues = subQueues.concat(["default"]);
}
subQueues = Array.from(new Set(subQueues));
this.queueNames = subQueues;
this.__queues__ = {};
this.runOnAdd = runOnAdd;
subQueues.forEach(
function (name) {
this.__queues__[name] = new SubQueue();
}.bind(this),
);
}
util.inherits(Queue, events.EventEmitter);
/**
* Create a new sub-queue.
* @param {String} name The sub-queue to create
* @param {String} [before] Add the new sub-queue before the this sub-queue.
* Otherwise the new sub-queue will be added last.
*/
Queue.prototype.addSubQueue = function (name, before) {
if (this.__queues__[name]) {
// Sub-queue already exists
return;
}
if (!before) {
// Add at last place.
this.queueNames.push(name);
this.__queues__[name] = new SubQueue();
return;
}
if (!this.__queues__[before] || this.queueNames.indexOf(before) === -1) {
throw new Error("sub-queue " + before + " not found");
}
// Add new sub-queue into the array.
this.queueNames.splice(this.queueNames.indexOf(before), 0, name);
this.__queues__[name] = new SubQueue();
};
/**
* Add a task to a queue.
* @param {String} [name='default'] The sub-queue to append the task
* @param {Function} task
* @param {Object} [opt] Options hash
* @param {String} [opt.once] If a task with the same `once` value is inside the
* queue, don't add this task.
* @param {Boolean} [opt.run] If `run` is false, don't run the task.
*/
Queue.prototype.add = function (name, task, opt) {
if (typeof name !== "string") {
opt = task;
task = name;
name = "default";
}
this.__queues__[name].push(task, opt);
// don't run the tasks if `opt.run` is false
var run = (opt || {}).run;
run = run === undefined ? this.runOnAdd : run;
if (!run) return;
this.start();
};
/**
* Schedule run() to be executed.
*/
Queue.prototype.start = function () {
if (this.running) return;
setImmediate(this.run.bind(this));
};
/**
* Start emptying the queues
* Tasks are always run from the higher priority queue down to the lowest. After each
* task complete, the process is re-runned from the first queue until a task is found.
*
* Tasks are passed a `callback` method which should be called once the task is over.
*/
Queue.prototype.run = function () {
if (this.running) return;
this.running = true;
this._exec(
function () {
this.running = false;
if (
Object.values(this.__queues__).find(
(queue) => queue.__queue__.length > 0,
) === undefined
) {
this.emit("end");
} else {
this.emit("paused");
}
}.bind(this),
function (error) {
this.running = false;
if (error) {
this.emit("error", error);
} else {
this.running = false;
this.emit("paused");
}
}.bind(this),
);
};
/**
* Pause the queue
*/
Queue.prototype.pause = function () {
this.running = false;
};
Queue.prototype._exec = function (done, stop) {
var pointer = -1;
var names = this.queueNames;
var next = function next() {
if (!this.running) return done();
pointer++;
if (pointer >= names.length) return done();
this.__queues__[names[pointer]].run(
next.bind(this),
this._exec.bind(this, done, stop),
stop,
);
}.bind(this);
next();
};