215 lines
4.5 KiB
JavaScript
215 lines
4.5 KiB
JavaScript
|
function CronJob(CronTime, spawn) {
|
||
|
function fnWrap(cmd) {
|
||
|
var command;
|
||
|
var args;
|
||
|
|
||
|
switch (typeof cmd) {
|
||
|
case 'string':
|
||
|
args = cmd.split(' ');
|
||
|
command = args.shift();
|
||
|
|
||
|
return spawn.bind(undefined, command, args);
|
||
|
|
||
|
case 'object':
|
||
|
command = cmd && cmd.command;
|
||
|
if (command) {
|
||
|
args = cmd.args;
|
||
|
var options = cmd.options;
|
||
|
|
||
|
return spawn.bind(undefined, command, args, options);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return cmd;
|
||
|
}
|
||
|
|
||
|
function CJ(
|
||
|
cronTime,
|
||
|
onTick,
|
||
|
onComplete,
|
||
|
startNow,
|
||
|
timeZone,
|
||
|
context,
|
||
|
runOnInit,
|
||
|
utcOffset,
|
||
|
unrefTimeout
|
||
|
) {
|
||
|
var _cronTime = cronTime;
|
||
|
var argCount = 0;
|
||
|
for (var i = 0; i < arguments.length; i++) {
|
||
|
if (arguments[i] !== undefined) {
|
||
|
argCount++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (typeof cronTime !== 'string' && argCount === 1) {
|
||
|
// crontime is an object...
|
||
|
onTick = cronTime.onTick;
|
||
|
onComplete = cronTime.onComplete;
|
||
|
context = cronTime.context;
|
||
|
startNow = cronTime.start || cronTime.startNow || cronTime.startJob;
|
||
|
timeZone = cronTime.timeZone;
|
||
|
runOnInit = cronTime.runOnInit;
|
||
|
_cronTime = cronTime.cronTime;
|
||
|
utcOffset = cronTime.utcOffset;
|
||
|
unrefTimeout = cronTime.unrefTimeout;
|
||
|
}
|
||
|
|
||
|
this.context = context || this;
|
||
|
this._callbacks = [];
|
||
|
this.onComplete = fnWrap(onComplete);
|
||
|
this.cronTime = new CronTime(_cronTime, timeZone, utcOffset);
|
||
|
this.unrefTimeout = unrefTimeout;
|
||
|
|
||
|
addCallback.call(this, fnWrap(onTick));
|
||
|
|
||
|
if (runOnInit) {
|
||
|
this.lastExecution = new Date();
|
||
|
fireOnTick.call(this);
|
||
|
}
|
||
|
|
||
|
if (startNow) {
|
||
|
start.call(this);
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
var addCallback = function (callback) {
|
||
|
if (typeof callback === 'function') {
|
||
|
this._callbacks.push(callback);
|
||
|
}
|
||
|
};
|
||
|
CJ.prototype.addCallback = addCallback;
|
||
|
|
||
|
CJ.prototype.setTime = function (time) {
|
||
|
if (typeof time !== 'object') {
|
||
|
// crontime is an object...
|
||
|
throw new Error('time must be an instance of CronTime.');
|
||
|
}
|
||
|
this.stop();
|
||
|
this.cronTime = time;
|
||
|
this.start();
|
||
|
};
|
||
|
|
||
|
CJ.prototype.nextDate = function () {
|
||
|
return this.cronTime.sendAt();
|
||
|
};
|
||
|
|
||
|
var fireOnTick = function () {
|
||
|
for (var i = this._callbacks.length - 1; i >= 0; i--) {
|
||
|
this._callbacks[i].call(this.context, this.onComplete);
|
||
|
}
|
||
|
};
|
||
|
CJ.prototype.fireOnTick = fireOnTick;
|
||
|
|
||
|
CJ.prototype.nextDates = function (i) {
|
||
|
return this.cronTime.sendAt(i);
|
||
|
};
|
||
|
|
||
|
var start = function () {
|
||
|
if (this.running) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var MAXDELAY = 2147483647; // The maximum number of milliseconds setTimeout will wait.
|
||
|
var self = this;
|
||
|
var timeout = this.cronTime.getTimeout();
|
||
|
var remaining = 0;
|
||
|
var startTime;
|
||
|
|
||
|
if (this.cronTime.realDate) {
|
||
|
this.runOnce = true;
|
||
|
}
|
||
|
|
||
|
function _setTimeout(timeout) {
|
||
|
startTime = Date.now();
|
||
|
self._timeout = setTimeout(callbackWrapper, timeout);
|
||
|
if (self.unrefTimeout && typeof self._timeout.unref === 'function') {
|
||
|
self._timeout.unref();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The callback wrapper checks if it needs to sleep another period or not
|
||
|
// and does the real callback logic when it's time.
|
||
|
function callbackWrapper() {
|
||
|
var diff = startTime + timeout - Date.now();
|
||
|
|
||
|
if (diff > 0) {
|
||
|
var newTimeout = self.cronTime.getTimeout();
|
||
|
|
||
|
if (newTimeout > diff) {
|
||
|
newTimeout = diff;
|
||
|
}
|
||
|
|
||
|
remaining += newTimeout;
|
||
|
}
|
||
|
|
||
|
// If there is sleep time remaining, calculate how long and go to sleep
|
||
|
// again. This processing might make us miss the deadline by a few ms
|
||
|
// times the number of sleep sessions. Given a MAXDELAY of almost a
|
||
|
// month, this should be no issue.
|
||
|
self.lastExecution = new Date();
|
||
|
if (remaining) {
|
||
|
if (remaining > MAXDELAY) {
|
||
|
remaining -= MAXDELAY;
|
||
|
timeout = MAXDELAY;
|
||
|
} else {
|
||
|
timeout = remaining;
|
||
|
remaining = 0;
|
||
|
}
|
||
|
|
||
|
_setTimeout(timeout);
|
||
|
} else {
|
||
|
// We have arrived at the correct point in time.
|
||
|
|
||
|
self.running = false;
|
||
|
|
||
|
// start before calling back so the callbacks have the ability to stop the cron job
|
||
|
if (!self.runOnce) {
|
||
|
self.start();
|
||
|
}
|
||
|
|
||
|
self.fireOnTick();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (timeout >= 0) {
|
||
|
this.running = true;
|
||
|
|
||
|
// Don't try to sleep more than MAXDELAY ms at a time.
|
||
|
|
||
|
if (timeout > MAXDELAY) {
|
||
|
remaining = timeout - MAXDELAY;
|
||
|
timeout = MAXDELAY;
|
||
|
}
|
||
|
|
||
|
_setTimeout(timeout);
|
||
|
} else {
|
||
|
this.stop();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
CJ.prototype.start = start;
|
||
|
|
||
|
CJ.prototype.lastDate = function () {
|
||
|
return this.lastExecution;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Stop the cronjob.
|
||
|
*/
|
||
|
CJ.prototype.stop = function () {
|
||
|
if (this._timeout) clearTimeout(this._timeout);
|
||
|
this.running = false;
|
||
|
if (typeof this.onComplete === 'function') {
|
||
|
this.onComplete();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
return CJ;
|
||
|
}
|
||
|
|
||
|
module.exports = CronJob;
|