directmail
Version:
Sendmail replacement, sends mail directly to recipients SMTP server
118 lines (105 loc) • 3.14 kB
JavaScript
'use strict';
// expose to the world
module.exports = function() {
return new Queue();
};
/**
* Creates a queue object
*
* @constructor
*/
function Queue() {
this._instantQueue = [];
this._sortedQueue = [];
this._shiftTimer = null;
this._callbackQueue = [];
}
/**
* Sets a callback to be run when something comes available from the queue
*
* @param {Function} callback Callback function to run with queue element as an argument
*/
Queue.prototype.get = function(callback) {
if (this._instantQueue.length) {
callback(this._instantQueue.pop());
} else {
this._callbackQueue.unshift(callback);
}
};
/**
* Adds an element to the queue. If delay (ms) is set, the data will not be available before
* specified delay has passed. Otherwise the data will be available for processing immediatelly.
*
* @param {Mixed} data Value to be queued
* @param {Number} [delay] If set, delay the availability of the data by {delay} milliseconds
*/
Queue.prototype.insert = function(data, delay) {
var container, added = -1;
if (typeof delay != 'number') {
this._instantQueue.unshift(data);
this._processInsert();
return true;
} else {
container = {
data: data,
available: Date.now() + delay
};
for (var i = 0, len = this._sortedQueue.length; i < len; i++) {
if (this._sortedQueue[i].available >= container.available) {
this._sortedQueue.splice(i, 0, container);
added = i;
break;
}
}
if (added < 0) {
this._sortedQueue.push(container);
added = 0;
}
if (added === 0) {
this._updateShiftTimer();
}
}
};
/**
* Clears previous timer and creates a new one (if needed) to process the element
* in the queue that needs to be processed first.
*/
Queue.prototype._updateShiftTimer = function() {
var nextShift, now = Date.now();
clearTimeout(this._shiftTimer);
if (!this._sortedQueue.length) {
return;
}
nextShift = this._sortedQueue[0].available;
if (nextShift <= now) {
this._shiftSorted();
} else {
setTimeout(this._shiftSorted.bind(this),
// add +15ms to ensure that data is already available when the timer is fired
this._sortedQueue[0].available - Date.now() + 15);
}
};
/**
* Moves an element from the delayed queue to the immediate queue if an elmenet
* becomes avilable
*/
Queue.prototype._shiftSorted = function() {
var container;
if (!this._sortedQueue.length) {
return;
}
if (this._sortedQueue[0].available <= Date.now()) {
container = this._sortedQueue.shift();
this.insert(container.data);
}
this._updateShiftTimer();
};
/**
* If data from a queue is available and a callback is set, run the callback
* with available data
*/
Queue.prototype._processInsert = function() {
if (this._instantQueue.length && this._callbackQueue.length) {
this._callbackQueue.pop()(this._instantQueue.pop());
}
};