问题描述:最近看pomelo的一个子模块pomelo-scheduler,这个子模块完全独立的,并不需要了解其他的知识,并且有附带有Test程序。
git地址:https://github.com/NetEase/pomelo-scheduler
pomelo-scheduler中有一个模块:cronTrigger.js
对应一个Test程序:cronTriggerTest.js
但是运行这个Test会出错:
"D:\Program Files (x86)\JetBrains\WebStorm 2016.1.3\bin\runnerw.exe" "D:\Program Files\nodejs\node.exe" cronTriggerTest.js
d:\Demo\pomelo\node_modules\pomelo-scheduler\test\cronTriggerTest.js:22
var timer = cronTrigger.decodeTrigger('0 0 0 1 0 2-5');
^
TypeError: undefined is not a function
at nextTimeTest (d:\Demo\pomelo\node_modules\pomelo-scheduler\test\cronTriggerTest.js:22:27)
at test (d:\Demo\pomelo\node_modules\pomelo-scheduler\test\cronTriggerTest.js:133:3)
at Object.<anonymous> (d:\Demo\pomelo\node_modules\pomelo-scheduler\test\cronTriggerTest.js:137:1)
at Module._compile (module.js:460:26)
at Object.Module._extensions..js (module.js:478:10)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
at Function.Module.runMain (module.js:501:10)
at startup (node.js:129:16)
at node.js:814:3
使用console输出:
function nextTimeTest(count){
console.log(cronTrigger);
var timer = cronTrigger.decodeTrigger('0 0 0 1 0 2-5');
输出结果为:
{ createTrigger: [Function: createTrigger] }
刚学js,错误找了半天,也没发现是哪里出错了。
函数明明定义好了也导出了,为什么用的时候会不识别?
附cronTrigger.js代码:
/**
* This is the trigger used to decode the cronTimer and calculate the next excution time of the cron Trigger.
*/
var logger = require('log4js').getLogger(__filename);
var SECOND = 0;
var MIN = 1;
var HOUR = 2;
var DOM = 3;
var MONTH = 4;
var DOW = 5;
var Limit = [[0,59],[0,59],[0,24],[1,31],[0,11],[0,6]];
/**
* The constructor of the CronTrigger
* @param trigger The trigger str used to build the cronTrigger instance
*/
var CronTrigger = function(trigger, job){
this.trigger = this.decodeTrigger(trigger);
this.nextTime = this.nextExcuteTime(Date.now());
this.job = job;
};
var pro = CronTrigger.prototype;
/**
* Get the current excuteTime of trigger
*/
pro.excuteTime = function(){
return this.nextTime;
};
/**
* Caculate the next valid cronTime after the given time
* @param The given time point
* @return The nearest valid time after the given time point
*/
pro.nextExcuteTime = function(time){
//add 1s to the time so it must be the next time
time = !!time?time:this.nextTime;
time += 1000;
var cronTrigger = this.trigger;
var date = new Date(time);
date.setMilliseconds(0);
outmost:
while(true){
if(date.getFullYear() > 2999){
logger.error("Can't compute the next time, exceed the limit");
return null;
}
if(!timeMatch(date.getMonth(), cronTrigger[MONTH])){
var nextMonth = nextCronTime(date.getMonth(), cronTrigger[MONTH]);
if(nextMonth == null)
return null;
if(nextMonth <= date.getMonth()){
date.setYear(date.getFullYear() + 1);
date.setMonth(0);
date.setDate(1);
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
continue;
}
date.setDate(1);
date.setMonth(nextMonth);
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
}
if(!timeMatch(date.getDate(), cronTrigger[DOM]) || !timeMatch(date.getDay(), cronTrigger[DOW])){
var domLimit = getDomLimit(date.getFullYear(), date.getMonth());
do{
var nextDom = nextCronTime(date.getDate(), cronTrigger[DOM]);
if(nextDom == null)
return null;
//If the date is in the next month, add month
if(nextDom <= date.getDate() || nextDom > domLimit){
date.setDate(1);
date.setMonth(date.getMonth() + 1);
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
continue outmost;
}
date.setDate(nextDom);
}while(!timeMatch(date.getDay(), cronTrigger[DOW]));
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
}
if(!timeMatch(date.getHours(), cronTrigger[HOUR])){
var nextHour = nextCronTime(date.getHours(), cronTrigger[HOUR]);
if(nextHour <= date.getHours()){
date.setDate(date.getDate() + 1);
date.setHours(nextHour);
date.setMinutes(0);
date.setSeconds(0);
continue;
}
date.setHours(nextHour);
date.setMinutes(0);
date.setSeconds(0);
}
if(!timeMatch(date.getMinutes(), cronTrigger[MIN])){
var nextMinute = nextCronTime(date.getMinutes(), cronTrigger[MIN]);
if(nextMinute <= date.getMinutes()){
date.setHours(date.getHours() + 1);
date.setMinutes(nextMinute);
date.setSeconds(0);
continue;
}
date.setMinutes(nextMinute);
date.setSeconds(0);
}
if(!timeMatch(date.getSeconds(), cronTrigger[SECOND])){
var nextSecond = nextCronTime(date.getSeconds(), cronTrigger[SECOND]);
if(nextSecond <= date.getSeconds()){
date.setMinutes(date.getMinutes() + 1);
date.setSeconds(nextSecond);
continue;
}
date.setSeconds(nextSecond);
}
break;
}
this.nextTime = date.getTime();
return this.nextTime;
};
/**
* return the next match time of the given value
* @param value The time value
* @param cronTime The cronTime need to match
* @return The match value or null if unmatch(it offten means an error occur).
*/
function nextCronTime(value, cronTime){
value += 1;
if(typeof(cronTime) == 'number'){
if(cronTime == -1)
return value;
else
return cronTime;
}else if(typeof(cronTime) == 'object' && cronTime instanceof Array){
if(value <= cronTime[0] || value > cronTime[cronTime.length -1])
return cronTime[0];
for(var i = 0; i < cronTime.length; i++)
if(value <= cronTime[i])
return cronTime[i];
}
logger.warn('Compute next Time error! value :' + value + ' cronTime : ' + cronTime);
return null;
}
/**
* Match the given value to the cronTime
* @param value The given value
* @param cronTime The cronTime
* @return The match result
*/
function timeMatch(value, cronTime){
if(typeof(cronTime) == 'number'){
if(cronTime == -1)
return true;
if(value == cronTime)
return true;
return false;
}else if(typeof(cronTime) == 'object' && cronTime instanceof Array){
if(value < cronTime[0] || value > cronTime[cronTime.length -1])
return false;
for(var i = 0; i < cronTime.length; i++)
if(value == cronTime[i])
return true;
return false;
}
return null;
}
/**
* Decude the cronTrigger string to arrays
* @param cronTimeStr The cronTimeStr need to decode, like "0 12 * * * 3"
* @return The array to represent the cronTimer
*/
pro.decodeTrigger = function(cronTimeStr){
var cronTimes = cronTimeStr.split(/\s+/);
if(cronTimes.length != 6){
console.log('error');
return null;
}
for(var i = 0; i < cronTimes.length; i++){
cronTimes[i] = (this.decodeTimeStr(cronTimes[i], i));
if(!checkNum(cronTimes[i], Limit[i][0], Limit[i][1])){
logger.error('Decode crontime error, value exceed limit!' +
JSON.stringify({cronTime: cronTimes[i], limit:Limit[i]}));
return null;
}
}
return cronTimes;
}
/**
* Decode the cron Time string
* @param timeStr The cron time string, like: 1,2 or 1-3
* @return A sorted array, like [1,2,3]
*/
pro.decodeTimeStr = function(timeStr, type){
var result = {};
var arr = [];
if(timeStr=='*'){
return -1;
}else if(timeStr.search(',')>0){
var timeArr = timeStr.split(',');
for(var i = 0; i < timeArr.length; i++){
var time = timeArr[i];
if(time.match(/^\d+-\d+$/)){
decodeRangeTime(result, time);
}else if(time.match(/^\d+\/\d+/)){
decodePeriodTime(result, time, type);
}else if(!isNaN(time)){
var num = Number(time);
result[num] = num;
}else
return null;
}
}else if(timeStr.match(/^\d+-\d+$/)){
decodeRangeTime(result, timeStr);
}else if(timeStr.match(/^\d+\/\d+/)){
decodePeriodTime(result, timeStr, type);
}else if(!isNaN(timeStr)){
var num = Number(timeStr);
result[num] = num;
}else{
return null;
}
for(var key in result){
arr.push(result[key]);
}
arr.sort(function(a, b){
return a - b;
});
return arr;
}
/**
* Decode time range
* @param map The decode map
* @param timeStr The range string, like 2-5
*/
function decodeRangeTime(map, timeStr){
var times = timeStr.split('-');
times[0] = Number(times[0]);
times[1] = Number(times[1]);
if(times[0] > times[1]){
console.log("Error time range");
return null;
}
for(var i = times[0]; i <= times[1]; i++){
map[i] = i;
}
}
/**
* Compute the period timer
*/
function decodePeriodTime(map, timeStr, type){
var times = timeStr.split('/');
var min = Limit[type][0];
var max = Limit[type][1];
var remind = Number(times[0]);
var period = Number(times[1]);
if(period==0)
return;
for(var i = min; i <= max; i++){
if(i%period == remind)
map[i] = i;
}
}
/**
* Check if the numbers are valid
* @param nums The numbers array need to check
* @param min Minimus value
* @param max Maximam value
* @return If all the numbers are in the data range
*/
function checkNum(nums, min, max){
if(nums == null)
return false;
if(nums == -1)
return true;
for(var i = 0; i < nums.length; i++){
if(nums[i]<min || nums[i]>max)
return false;
}
return true;
}
/**
* Get the date limit of given month
* @param The given year
* @month The given month
* @return The date count of given month
*/
function getDomLimit(year, month){
var date = new Date(year, month+1, 0);
return date.getDate();
}
/**
* Create cronTrigger
* @param trigger The Cron Trigger string
* @return The Cron trigger
*/
function createTrigger(trigger, job){
return new CronTrigger(trigger, job);
}
module.exports.createTrigger = createTrigger;
附cronTriggerTest.js 代码:
var cronTrigger = require('../lib/cronTrigger');
var SECOND = 0;
var MIN = 1;
var HOUR = 2;
var DOM = 3;
var MONTH = 4;
var DOW = 5;
function decoderTest(){
var result = [];
result.push(cronTrigger.decodeTrigger('12 2 3,4,5 4 5 1'));
result.push(cronTrigger.decodeTrigger('* 1-3,2-9,4 3,4,5 4-9 5 1'));
result.push(cronTrigger.decodeTrigger('12 2 3 4 5 1'));
console.log(result);
}
function nextTimeTest(count){
console.log(cronTrigger);
var timer = cronTrigger.decodeTrigger('0 0 0 1 0 2-5');
var value = Date.now();
console.log(timer);
// console.log([1,2] instanceof Array);
var r1, r2;
var start = Date.now();
for(var i = 0; i < count; i++)
r1 = cronTrigger.nextTime(value, timer);
var end = Date.now();
console.log("first run time : " + (end-start));
var start = Date.now();
for(var i = 0; i < count; i++)
r2 = nextExcuteTimeTest(value, timer);
var end = Date.now();
console.log("second run time : " + (end-start));
console.log("first run time:" + r1);
console.log("second run time:" + r2);
}
function nextExcuteTimeTest(time, cronTrigger){
var next = new Date(time + 1000);
while (true)
{
if (!timeMatch(next.getMonth(), cronTrigger[MONTH]))
{
next.setMonth(next.getMonth()+1);
next.setDate(1);
next.setHours(0);
next.setMinutes(0);
next.setSeconds(0);
continue;
}
if (!timeMatch(next.getDate(), cronTrigger[DOM]))
{
next.setDate(next.getDate() + 1);
next.setHours(0);
next.setMinutes(0);
next.setSeconds(0);
continue;
}
if (!timeMatch(next.getDay(), cronTrigger[DOW]))
{
next.setDate(next.getDate() + 1);
next.setHours(0);
next.setMinutes(0);
next.setSeconds(0);
continue;
}
if (!timeMatch(next.getHours(), cronTrigger[HOUR]))
{
next.setHours(next.getHours() + 1);
next.setMinutes(0);
next.setSeconds(0);
continue;
}
if (!timeMatch(next.getMinutes(), cronTrigger[MIN]))
{
next.setMinutes(next.getMinutes() + 1);
next.setSeconds(0);
continue;
}
if (!timeMatch(next.getSeconds(), cronTrigger[SECOND]))
{
next.setSeconds(next.getSeconds()+1);
continue;
}
break;
}
return next;
}
function getDomLimitTest(y1,y2,m1,m2){
for(var year = y1; year <= y2; year++)
for(var month = m1; month <= m2; month++){
console.log(year + "." + (month +1)+ " limit : " + cronTrigger.getDomLimit(year,month));
}
}
function timeMatch(value, cronTime){
// console.log("match value " + value + ' cronTime ' + cronTime);
if(typeof(cronTime) == 'number'){
if(cronTime == -1)
return true;
if(value == cronTime)
return true;
return false;
}else if(typeof(cronTime) == 'object' && cronTime instanceof Array){
if(value < cronTime[0] || value > cronTime[cronTime.length -1])
return false;
for(var i = 0; i < cronTime.length; i++)
if(value == cronTime[i])
return true;
return false;
}
return null;
}
function test(){
nextTimeTest(100);
// getDomLimitTest(1800,2002,1,1);
}
test();
cronTrigger 是个构造函数不能拿来直接用 得先new一下或者调用工厂方法createTrigger实例化出来。test应该是在实现修改后没有更新。