优雅的使用node-schedule(上)

前言

在 Javascript 中,有时候我们有定时事务的需求,自己借助setTimeout和setInterval来实现的化太过麻烦,node-schedule是一个非常不错的npm包,可以帮助我们快速的创建和管理定时事务。
本文主要介绍 node-schedule 的基础用法。

node-schedule介绍

安装

npm install node-schedule

创建计划

需要用到scheduleJob函数,会返回一个Job实例对象:

function 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个域:

*    *    *    *    *    *
┬    ┬    ┬    ┬    ┬    ┬
│    │    │    │    │    │
│    │    │    │    │    └ 星期几(相对于周日) (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代表:全年的每天每秒都触发
const rule = '* * * * * *';
const job = schedule.scheduleJob(rule,()=>{});
  • 每N秒执行
    */1 2代表从头开始,每1秒触发一次
const rule = '*/1 * * * * *';
const job = schedule.scheduleJob(rule,()=>{});
  • 每个半点时触发,在第30分钟内的每秒执行
const rule = '* 30 * * * *';
const job = schedule.scheduleJob(rule,()=>{});
  • 每个半点15秒时触发,在第30分钟内的每10秒执行
const rule = '*/10 30 * * * *';
const job = schedule.scheduleJob(rule,()=>{});
  • 每个1秒,3秒和9秒执行
const rule = '1,3,9 * * * * *';
const job = schedule.scheduleJob(rule,()=>{});
  • 每个星期日触发,全天每秒都执行
    0和7都是星期日,1-6对应周一到周六,此时必须把日域用 ? 覆盖,如果日用 * ,意味着每天都触发,会覆盖周日的限制3
const rule = '* * * ? * 0';
const job = schedule.scheduleJob(rule,()=>{});
  • 每月第4个星期日触发,全体每秒都执行
    #N 4在后面指定第几个星期几
const rule = '* * * ? * 0#4';
const job = schedule.scheduleJob(rule,()=>{});
  • 每周一早上的零点执行
const rule = '0 0 0 ? * 1';
const job = schedule.scheduleJob(rule,()=>{});
  • 每0~10分内,每分钟的第0秒触发
    0-10 5代表这个范围,并且是闭区间
const rule = '0 0-10 * * * *';
const job = schedule.scheduleJob(rule,()=>{});
  • 每月的20日触发
    此时必须把星期域用 ? 覆盖,如果周几用 * ,意味着每个周几都触发,那就是每天,会覆盖20日的限制
const rule = '* * * 20 * ?';
const job = schedule.scheduleJob(rule,()=>{});

注意

  • node-schedule中不支持cron的 LW 语法(最后和最近)
  • node-schedule中不支持Year,因此只有6个域,默认每年
  • 创建了计划后,到点就会触发了,Job没有run,start之类的方法
  • 到达触发时间点时,node-schedule会以同样的方式创建一个新Job

cron表达式很简洁,不过可读性也是比较低,每次看到都要思考一下,我们来看一下别的rule写法

基于Date的规则

假设您非常希望在一个精确到某一个时间点上的秒数的仅触发一次的计划,Date是不错的选择

const 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对象
let rule = new RecurrenceRule();

Recurrence的构造函数:

function Recurrence(year, month, date, dayOfWeek, hour, minute, second, tz)

因此你也可以在构造的时候就把参数传进去,如果没传的,默认是每

  • 设置重复时刻
    每个域可以赋的值和cron中基本类似,不过有一下几点区别:
  • 除了tz,其他都必须是数字
  • dayOfWeek 范围是 0~6,不再支持7作为Sunday
  • month 范围是 0~11 而非1~12
  • 不能像cron那样设置第几个周五这样子
  • 支持时区设置,点击查看所有可以作为tz的值

如果同一个域下可以有多个值,则须要把所有的值放在一个数组中赋给该域:

//每秒触发
rule.second = [1,2,3,... ...,60];

如果只有两三个值那还好,像上面这样的60个值手都麻了。如果是连续的值,node-schedule提供了一个Range函数用于创建连续的元素:

//第0,1,2秒和10~20秒间的每秒触发
rule.second = [0,1,2,new schedule.Range(10,20)];  //必须作为[]的元素,因为本身不是一个数组

Range返回的到底是什么?我们来打印一个RecurrentRule对象看一下:

let rule = new schedule.RecurrenceRule();
rule.second = [0,1,2,new schedule.Range(10,20)];
console.log(JSON.stringfy(rule));

输出:

 {"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对象,是这么个东西:

{
	start: number,  //起点
	end: number,  //终点
	step: number  //步长
}

所以我们在设置连续的值时,也可以按这个结构写:

let rule = new schedule.RecurrenceRule();
//每秒触发
rule.second = [{start: 0, end: 60, step: 1}];
console.log(JSON.stringfy(rule));

更方便的创建
你还可以直接使用键值式对象当作RecurrenceRule作为rule:

const job = schedule.scheduleJob(
	{
		hour: 14,
		minute: 30,
		dayOfWeek: 0
	}, 
	function() {
		//...
	}
);
基于RecurrenceSpecDateRange的规则
源码里面没看到,可能已废弃
基于RecurrenceSpecObjLit的规则
源码里面没看到,可能已废弃

结束计划

调用 Job 实例中的 cancel 方法即可结束计划的运行

job.cancel();

延迟启动/预设结束

采用键值对象作为rule时可以这样:rule键单独存储规则,另设启动时刻和结束时刻

const 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
const job = schedule.scheduleJob("* * * * * *",()=>{
	console.log("任务进行中...");
});
  • 当然你将任务内容绑定在监听到Job运行完时触发的函数内也无伤大雅(状态监听稍后讲)
this.job.on("run",()=>{
	console.log("任务进行中...");
	console.log("任务结束");
});

不过这样做的前提是,你不需要在任务内容执行完后返回一些相关的数据和信息。因此原则上规范的做法是,把任务的内容写在创建时的callback内

状态监听

总共有5个事件可以监听(其实创建也能算一个),不说什么了,直接摆代码:

//这个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优雅的结束所有的计划:
schedule.gracefulSgutdown();

你也可以在系统崩溃时,执行gracefulShutdown

process.on('SIGINT', function () { 
  schedule.gracefulShutdown()
  .then(() => process.exit(0))
}
  • schedule.cancelJob(job: string | schedule.Job)
    你也可以让schedule通过指定的job名或者实例去终止指定的Job
schedule.cancelJob("jobtest");
  • job.reschedule(rule)
    以新的规则重新规划这项Job

脚注


  1. Cron表达式是一种被多个空格隔成多个域的字符串,每一个域由数字和特殊字符组成,代表一种含义,通常用于指定时间规则 ↩︎

  2. S/NS 表示从该域最起始的位置S处触发一次,接着每隔N触发一次,5/20,5触发,然后25,45… ↩︎

  3. 因为日域和周几域都是关于天的规则,因此会产生冲突,此时就需要用 ? 把其中一项的优先级降低 ↩︎

  4. #N 只能用于星期几的那个域里面 ↩︎

  5. -代表范围,并且是闭区域 ↩︎

  6. 这个函数可以传入参数,但说实在的,没有必要 ↩︎

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
node-schedule 是一个用于在 Node.js 中进行定时任务调度的库。它允许你创建各种类型的定时任务,例如执行一次性任务、按照固定时间间隔重复执行任务,或者按照特定的时间规则执行任务。 你可以使用 node-schedule 定义一个定时任务,并指定任务的执行时间。例如,你可以创建一个每天早上 8 点执行的任务,或者每隔一小时执行一次的任务。node-schedule 提供了一些简单易用的方法来定义这些时间规则,使得任务调度变得非常方便。 以下是一个使用 node-schedule 的简单示例: ```javascript const schedule = require('node-schedule'); // 创建一个每天早上 8 点执行的任务 const job = schedule.scheduleJob('0 8 * * *', function() { console.log('任务执行中...'); }); // 创建一个每隔一小时执行一次的任务 const job2 = schedule.scheduleJob('0 * * * *', function() { console.log('任务执行中...'); }); ``` 在上面的示例中,我们使用 `scheduleJob` 方法来创建定时任务。第一个参数是一个时间规则表达式,用来指定任务的执行时间。`'0 8 * * *'` 表示每天早上 8 点执行,`'0 * * * *'` 表示每隔一小时执行一次。 当指定的时间到达时,回调函数会被调用,任务会被执行。 除了以上的例子,node-schedule 还提供了其他一些功能,例如取消定时任务、修改定时任务的执行时间等。你可以查阅官方文档来获取更多关于 node-schedule 的详细信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值