npm地址:https://www.npmjs.com/package/cron
用途:如Linux系统有一个定时执行任务的工具cron,cron作者实现了类似的功能。
/** * Created by bamboo on 2016/4/15. */
var cronJob = require('cron').CronJob;
var job1 = new cronJob("* * * * * *", function () {
"use strict";
console.log("每1秒执行一次。。。。")
});
job1.start();
var job2 = new cronJob("*/5 * * * * *", function () {
"use strict";
console.log("每5秒执行一次。。。")
});
job2.start();
执行
npm install cron
node cron.js
执行结果
每1秒执行一次。。。。
每1秒执行一次。。。。
每1秒执行一次。。。。
每1秒执行一次。。。。
每1秒执行一次。。。。
每5秒执行一次。。。
每1秒执行一次。。。。
每1秒执行一次。。。。
每1秒执行一次。。。。
每1秒执行一次。。。。
var CronJob = require('cron').CronJob;
var job = new CronJob({
cronTime: '00 30 11 * * 1-5',
onTick: function() {
/*周一到周五,每天中午十一点半执行任务 * Runs every weekday (Monday through Friday) * at 11:30:00 AM. It does not run on Saturday * or Sunday. */
},
start: false,
timeZone: 'America/Los_Angeles'
});
job.start();
var spawn = require("child_process").spawn;
var job = new cronJob("* * */30 * * *", function () {
"use strict";
console.log("开始执行定时更新任务");
var update = spawn(process.execPath, [path.resolve(__dirname, 'update/all.js')]);
update.stdin.pipe(process.stdout);
update.stderr.pipe(process.stderr);
//进程结束时触发close事件
update.on('close', function (code) {
console.log("更新结束,代码为=%d", code)
})
});
job.start();
CronJob
constructor(cronTime, onTick, onComplete, start, timezone, context, runOnInit) - 第一个参数可以为一个JSON ,Json的内容为如上的参数 cronTime - [REQUIRED] -规定时间执行程序. onTick - [REQUIRED] - 要执行的程序. onComplete - [OPTIONAL] - 当这个任务执行完成时候执行的完成程序,在job被stop()的时候使用 start - [OPTIONAL] - 如果为true则该job立即执行 timeZone - [OPTIONAL] - 时区,如'America/Los_Angeles'会根据时区修正为正确的时间 context - [OPTIONAL] - 在执行ontick函数中使用. runOnInit - [OPTIONAL] - 如果为true则马上执行ontick函数 start - 开始执行任务 stop - 结束执行任务 CronTime constructor(time) time - [REQUIRED] - 设置执行ontick的时间.时间格式可以为corn syntax和Js Date对象
通过npm下载得到的源码,经过查看,发现主要代码的位置位于lib/cron.js中,按照阅读源码的习惯,首先找到exports的位置,然后看这个模块到底导出了什么?
if (exports) {
//直接返回一个任务
exports.job = function (cronTime, onTick, onComplete) {
return new CronJob(cronTime, onTick, onComplete);
}
//返回定时时长
exports.time = function (cronTime, timeZone) {
return new CronTime(cronTime, timeZone);
}
//返回下次执行的时间
exports.sendAt = function (cronTime) {
return exports.time(cronTime).sendAt();
}
exports.timeout = function (cronTime) {
return exports.time(cronTime).getTimeout();
}
//任务类
exports.CronJob = CronJob;
exports.CronTime = CronTime;
}
看到使用处的new cronJob(“* * * * * *”)
很想知道这到底意味着什么,查看源码得知CronTime的语法格式为f1 f2 f3 f4 f5 f6
分别代表
timeUnits = ['second', 'minute', 'hour', 'dayOfMonth', 'month', 'dayOfWeek'],
//秒钟,分钟,小时,一个月份中的第几天,月份,一个星期中的第几天
并存在对这些数值大小的限制,和日常中想的一样,限制的值如下
CronTime.constraints = [ [0, 59], [0, 59], [0, 23], [1, 31], [0, 11], [0, 6] ];
function CronJob(cronTime, onTick, onComplete, startNow, timeZone, context, runOnInit) {
var _cronTime = cronTime;
if (typeof cronTime != "string" && arguments.length == 1) {
//当第一个参数为一个对象的时候,其他的参数都在这个对象中
onTick = cronTime.onTick;
onComplete = cronTime.onComplete;
context = cronTime.context;
startNow = cronTime.start || cronTime.startNow || cronTime.startJob;//由这3值决定任务是否马上执行
timeZone = cronTime.timeZone;//时区
runOnInit = cronTime.runOnInit;
_cronTime = cronTime.cronTime;
}
this.context = (context || this);
this._callbacks = [];//ontick存放的位置
//onComplete,可能是一个函数,也可能是一个如shell(ls -a )的命令
//所有需要将解析
this.onComplete = command2function(onComplete);
this.cronTime = new CronTime(_cronTime, timeZone);
//添加回调
addCallback.call(this, command2function(onTick));
//如果true则马上执行ontick
if (runOnInit) fireOnTick.call(this);
//如果为true则马上执行job 注意比较2者的区别
if (startNow) start.call(this);
return this;
}
function command2function(cmd) {
switch (typeof cmd) {
case 'string':
var args = cmd.split(' ');
var command = args.shift();//删除第一个并返回元素
cmd = spawn.bind(undefined, command, args);
break;
case 'object':
var command = cmd && cmd.command;
if (command) {
var args = cmd.args;
var options = cmd.options;
console.log(command);
cmd = spawn.bind(undefined, command, args, options);
}
break;
}
return cmd
}
注意:代码中出现的spawn,正如大家所想的,那正是child_process模块的spawn方法。
var spawn = require('child_process').spawn;
关于这个child_process模块在nodeapi中可以查到
https://nodejs.org/download/docs/v5.7.0/api/child_process.html
关于addCallback函数只是一个简单的
var addCallback = function (callback) {
if (typeof callback == 'function') this._callbacks.push(callback);
}
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;
if (this.cronTime.realDate) this.runOnce = true;
//这个函数用来检查是否需要再休眠一次,和执行真实地回调逻辑,当时间到了的时候
function callbackWrapper() {
//如果仍然需要休眠,则计算休眠时长并休眠,这个操作将会消耗几ms的时间,这样的几ms的休眠时间对于定时时长长达几个month的来说,几乎没有影响。
//如果有
if (remaining) {
if (remaining > MAXDELAY) {
remaining -= MAXDELAY;
timeout = MAXDELAY;
} else {
timeout = remaining;
remaining = 0;
}
self._timeout = setTimeout(callbackWrapper, timeout);//timeout时间后再执行这个函数
//,没有,说明以及到了执行ontick函数的时候了
} else {
self.running = false;
//start before calling back so the callbacks have the ability to stop the cron job
//在回调之前开始,这样做能保证在进行回调的时候有能力将job停止
if (!(self.runOnce)) self.start();
self.fireOnTick();
//等价于 for (var i = (self._callbacks.length - 1); i >= 0; i--)
//self._callbacks[i].call(self.context, self.onComplete);
}
}
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;
}
this._timeout = setTimeout(callbackWrapper, timeout);
} else {
this.stop();
}
任务开始的地方:仔细分析这个函数的构成,可以发现,任务是根据timeout是否大于0来判断任务是否还在运行,如果正在运行的话,则将在timeout时间后调用ontick,此时将self.running设置成false,故可以猜测在stop()方法中,肯定设置了timeout的值,这样才会“停下来”。注意在源码的451行中this._timeout = setTimeout(callbackWrapper, timeout);
对于这个返回值, 将根据这个值在stop中执行clearTimeout操作
另一个值得注意的是:callbackWrapper这个函数做了什么工作,这个函数简单地来说就是检查是否需要再休眠,然后执行ontick函数,具体做法就是如果remaining,则说明还需要设置执行再休眠,重新timeout的值,然后执行ontick函数。多说无益,如果有兴趣学习下源码比什么源码分析都强呢。。
任务终结地如此简单
CronJob.prototype.stop = function () {
if (this._timeout)
clearTimeout(this._timeout);//清除
this.running = false;
if (typeof this.onComplete == 'function') this.onComplete();//如果是函数,则执行function
}
任务终结的时候,在最开始new CronJob的时候可能会对任务结束时进行回调,onComplete函数就是这个回调函数。
只做了部分源码分析,关于根据CronTime的内容,将在过些时间进行分析,其中还包括一个解析时间的moment模块。