优雅的使用node-schedule(上)
前言
在 Javascript 中,有时候我们有定时事务的需求,自己借助setTimeout和setInterval来实现的化太过麻烦,node-schedule是一个非常不错的npm包,可以帮助我们快速的创建和管理定时事务。
本文主要介绍 node-schedule 的基础用法。
node-schedule介绍
安装
copynpm install node-schedule
创建计划
需要用到scheduleJob
函数,会返回一个Job实例对象:
copyfunction scheduleJob(name: string, rule: ..., callback: function): schedule.Job
- name
任务名,当你没有指定时,它将以时间戳作为名字:'<Anonymous Job 1 2023-04-20T10:23:28.966Z>'
- rule:
任务调度的规则,支持多种形式的rule:- string - Cron表达式
- number
- schedule.RecurrenceRule
- Date
- callback
创建任务时的回调函数
可以通过scheduleJob(name, rule, callback)
或者scheduleJob(rule, callback)
创建计划。
调度规则
基于Cron表达式[1]的规则
在 node-schedule 中,采用的 cron 表达式包含6个域:
copy* * * * * * ┬ ┬ ┬ ┬ ┬ ┬ │ │ │ │ │ │ │ │ │ │ │ └ 星期几(相对于周日) (0 - 7) (0 or 7 is Sun) │ │ │ │ └───── 月(相对于年初) (1 - 12) │ │ │ └────────── 日(相对于月初) (1 - 31) │ │ └─────────────── 时(相对于天初第0时) (0 - 23) │ └──────────────────── 分(相对于时初第0分) (0 - 59) └───────────────────────── 秒(相对于分出第0秒) (0 - 59, OPTIONAL)
使用cron字符串作为rule时,建议写完整,可读性比较好。
定时循环
cron表达式作为rule时,用来指定每当某个/某些时刻触发任务的调度执行,譬如:
- 每秒执行
*
号代表的意思是每,因此这条rule代表:全年的每天每秒都触发
copyconst rule = '* * * * * *';
const job = schedule.scheduleJob(rule,()=>{});
- 每N秒执行
*/1
[2]代表从头开始,每1秒触发一次
copyconst rule = '*/1 * * * * *';
const job = schedule.scheduleJob(rule,()=>{});
- 每个半点时触发,在第30分钟内的每秒执行
copyconst rule = '* 30 * * * *';
const job = schedule.scheduleJob(rule,()=>{});
- 每个半点15秒时触发,在第30分钟内的每10秒执行
copyconst rule = '*/10 30 * * * *';
const job = schedule.scheduleJob(rule,()=>{});
- 每个1秒,3秒和9秒执行
copyconst rule = '1,3,9 * * * * *';
const job = schedule.scheduleJob(rule,()=>{});
- 每个星期日触发,全天每秒都执行
0和7都是星期日,1-6对应周一到周六,此时必须把日域用?
覆盖,如果日用*
,意味着每天都触发,会覆盖周日的限制[3]
copyconst rule = '* * * ? * 0';
const job = schedule.scheduleJob(rule,()=>{});
- 每月第4个星期日触发,全体每秒都执行
用#N
[4]在后面指定第几个星期几
copyconst rule = '* * * ? * 0#4';
const job = schedule.scheduleJob(rule,()=>{});
- 每周一早上的零点执行
copyconst rule = '0 0 0 ? * 1';
const job = schedule.scheduleJob(rule,()=>{});
- 每0~10分内,每分钟的第0秒触发
0-10
[5]代表这个范围,并且是闭区间
copyconst rule = '0 0-10 * * * *';
const job = schedule.scheduleJob(rule,()=>{});
- 每月的20日触发
此时必须把星期域用?
覆盖,如果周几用*
,意味着每个周几都触发,那就是每天,会覆盖20日的限制
copyconst rule = '* * * 20 * ?';
const job = schedule.scheduleJob(rule,()=>{});
注意:
- node-schedule中不支持cron的
L
和W
语法(最后和最近) - node-schedule中不支持Year,因此只有6个域,默认每年
- 创建了计划后,到点就会触发了,Job没有run,start之类的方法
- 到达触发时间点时,node-schedule会以同样的方式创建一个新Job
cron表达式很简洁,不过可读性也是比较低,每次看到都要思考一下,我们来看一下别的rule写法
基于Date的规则
假设您非常希望在一个精确到某一个时间点上的秒数的仅触发一次的计划,Date是不错的选择
copyconst schedule = require('node-schedule');
//2023年,4月,20日,23时,30分,0秒
const date = new Date(2023, 4, 20, 23, 30, 0);
const job = schedule.scheduleJob(date, ()=>{});
基于number的规则
可能有人还记得 rule 匹配的类型中number,其实它对应的是时间戳,如果接收到了number的rule,会被当作时间戳来生成相应的Date,接着通过基于Date的rule去创建计划
基于RecurrenceRule的规则
如果你的任务是定时重复执行的,并且你希望有比cron更高的可读性,你可以尝试使用RecurrenceRule对象作为rule
- 创建RecurrenceRule对象
copylet rule = new RecurrenceRule();
Recurrence的构造函数:
copyfunction Recurrence(year, month, date, dayOfWeek, hour, minute, second, tz)
因此你也可以在构造的时候就把参数传进去,如果没传的,默认是每
- 设置重复时刻
每个域可以赋的值和cron中基本类似,不过有一下几点区别: - 除了tz,其他都必须是数字
- dayOfWeek 范围是 0~6,不再支持7作为Sunday
- month 范围是 0~11 而非1~12
- 不能像cron那样设置第几个周五这样子
- 支持时区设置,点击查看所有可以作为tz的值
如果同一个域下可以有多个值,则须要把所有的值放在一个数组中赋给该域:
copy//每秒触发
rule.second = [1,2,3,... ...,60];
如果只有两三个值那还好,像上面这样的60个值手都麻了。如果是连续的值,node-schedule提供了一个Range函数用于创建连续的元素:
copy//第0,1,2秒和10~20秒间的每秒触发
rule.second = [0,1,2,new schedule.Range(10,20)]; //必须作为[]的元素,因为本身不是一个数组
Range返回的到底是什么?我们来打印一个RecurrentRule对象看一下:
copylet rule = new schedule.RecurrenceRule();
rule.second = [0,1,2,new schedule.Range(10,20)];
console.log(JSON.stringfy(rule));
输出:
copy{"recurs":true,"year":null,"month":null,"date":null,"dayOfWeek":null,"hour":null,"minute":null,"second":[0,1,2,{"start":10,"end":20,"step":1}]}
可以看到实际上Range函数返回的schedule.Range对象,是这么个东西:
copy{
start: number, //起点
end: number, //终点
step: number //步长
}
所以我们在设置连续的值时,也可以按这个结构写:
copylet rule = new schedule.RecurrenceRule();
//每秒触发
rule.second = [{start: 0, end: 60, step: 1}];
console.log(JSON.stringfy(rule));
更方便的创建
你还可以直接使用键值式对象当作RecurrenceRule作为rule:
copyconst job = schedule.scheduleJob(
{
hour: 14,
minute: 30,
dayOfWeek: 0
},
function() {
//...
}
);
基于RecurrenceSpecDateRange的规则
源码里面没看到,可能已废弃基于RecurrenceSpecObjLit的规则
源码里面没看到,可能已废弃结束计划
调用 Job 实例中的 cancel 方法即可结束计划的运行
copyjob.cancel();
延迟启动/预设结束
采用键值对象作为rule时可以这样:rule键单独存储规则,另设启动时刻和结束时刻
copyconst now = new Date();
const job = schedule.scheduleJob(
{ start: new Date(now.getTime()+5000), //相对于现在延迟5秒开始
end: new Date(now.getTime()+10000), //相对于开始后的5秒开始(对现在10秒)
rule
}, function() {
//...
}
);
虽然到了end时间点后任务不会再被触发,但计划依旧在执行中,毕竟这只是规则层面的。
任务内容
- 通常把任务的内容写在创建时的callback函数[6]内
copyconst job = schedule.scheduleJob("* * * * * *",()=>{
console.log("任务进行中...");
});
- 当然你将任务内容绑定在监听到Job运行完时触发的函数内也无伤大雅(状态监听稍后讲)
copythis.job.on("run",()=>{
console.log("任务进行中...");
console.log("任务结束");
});
不过这样做的前提是,你不需要在任务内容执行完后返回一些相关的数据和信息。因此原则上规范的做法是,把任务的内容写在创建时的callback内
状态监听
总共有5个事件可以监听(其实创建也能算一个),不说什么了,直接摆代码:
copy//这个job被我存储一个实例内,所以是this.job
this.job = schedule.scheduleJob(this.rule,
//2.在执行这里的回调
()=>{
console.log("任务运行...");
return "执行了一件node-schedule任务"; //可以被监听success的回调函数捕捉
});
//1.先执行 scheduled 回调
this.job.on("scheduled",()=>{
console.log("任务被调度");
//scheduled的回调函数中出的错不会被监听error所捕获
});
//3.再执行 run 回调
this.job.on("run",()=>{
console.log("任务结束");
});
//4.再执行 success 回调
this.job.on("success",(data)=>{
console.log(`任务成果: ${data}`);
console.log("任务成功!\n");
});
//5.只监听任务创建回调,run回调和success回调中产生的异常
this.job.on("error",(err)=>{
console.log(`[error][${new Date().toLocaleString()}]${err.message}`);
});
//6.计划被取消的那一刻执行 canceled
this.job.on("canceled",()=>{
console.log("计划结束!");
})
其他
- Job.cancelNext(true)
取消下一次任务,可以配合计数器使用 - schedule.gracefulShutdown()
通过schedule优雅的结束所有的计划:
copyschedule.gracefulSgutdown();
你也可以在系统崩溃时,执行gracefulShutdown
copyprocess.on('SIGINT', function () {
schedule.gracefulShutdown()
.then(() => process.exit(0))
}
- schedule.cancelJob(job: string | schedule.Job)
你也可以让schedule通过指定的job名或者实例去终止指定的Job
copyschedule.cancelJob("jobtest");
- job.reschedule(rule)
以新的规则重新规划这项Job
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步