优雅的使用node-schedule(上)

Posted evanpatchouli

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了优雅的使用node-schedule(上)相关的知识,希望对你有一定的参考价值。

在 Javascript 中,当我们有定时事务的需求,可以借助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函数用于创建连续的元素:

rule.second = [0,1,2,new schedule.Range(10,20)];  //必须作为[]的元素,本身不是一个数组

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

const job = schedule.scheduleJob(
	
		hour: 14,
		minute: 30,
		dayOfWeek: 0
	, 
	function ()
		//...
	
);
基于RecurrenceSpecDateRange的规则

源码里面没看到,可能已废弃

基于RecurrenceSpecObjLit的规则

源码里面没看到,可能已废弃

结束计划

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

job.cancel();

任务内容

  • 通常把任务的内容写在创建时的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("计划结束!");
)

脚注


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

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

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

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

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

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

Nodejs学习笔记--- 定时任务(node-schedule)

目录

写在之前

  在实际开发项目中,会遇到很多定时任务的工作。比如:定时导出某些数据、定时发送消息或邮件给用户、定时备份什么类型的文件等等

  一般可以写个定时器,来完成相应的需求,在node.js中自已实现也非常容易,接下来要介绍的是node-schedule来完成定时任务

  下面就用示例来说明一下node-schedule的用法。

 

  node-schedulehttps://github.com/node-schedule/node-schedule

  

  安装:

npm install node-schedule

技术分享

 

Cron风格定时器

var schedule = require(node-schedule);

function scheduleCronstyle(){
    schedule.scheduleJob(30 * * * * *, function(){
        console.log(scheduleCronstyle: + new Date());
    }); 
}

scheduleCronstyle();

  schedule.scheduleJob的回调函数中写入要执行的任务代码,一个定时器就完成了!

  下面我们再来讲讲Cron风格定时器传入的参数具体代表什么,先来看看上面执行结果,如下图

技术分享

  从输出结果可以看出,传入的‘30 * * * * *‘带来的结果是每分钟的30秒时都会执行,下面来看看这个传入参数分别代码什么

  通配符解释

*  *  *  *  *  *
┬ ┬ ┬ ┬ ┬ ┬
│ │ │ │ │  |
│ │ │ │ │ └ day of week (0 - 7) (0 or 7 is Sun)
│ │ │ │ └───── month (1 - 12)
│ │ │ └────────── day of month (1 - 31)
│ │ └─────────────── hour (0 - 23)
│ └──────────────────── minute (0 - 59)
└───────────────────────── second (0 - 59, OPTIONAL)

  6个占位符从左到右分别代表:秒、分、时、日、月、周几

  ‘*‘表示通配符,匹配任意,当秒是‘*‘时,表示任意秒数都触发,其它类推

  下面可以看看以下传入参数分别代表的意思

每分钟的第30秒触发: ‘30 * * * * *‘

每小时的1分30秒触发 :‘30 1 * * * *‘

每天的凌晨1点1分30秒触发 :‘30 1 1 * * *‘

每月的1日1点1分30秒触发 :‘30 1 1 1 * *‘

2016年的1月1日1点1分30秒触发 :‘30 1 1 1 2016 *‘

每周1的1点1分30秒触发 :‘30 1 1 * * 1‘

  这样很容易根据自已的需求用简短的代码去实现。

 

  Cron风格定时器-范围触发

  上面的传入参数占位符中还可以传入范围,比如下面示例

var schedule = require(‘node-schedule‘);

function scheduleCronstyle(){
    schedule.scheduleJob(‘1-10 * * * * *‘, function(){
        console.log(‘scheduleCronstyle:‘ + new Date());
    }); 
}

scheduleCronstyle();

  结果如下图:

技术分享

  从输出结果可以看出每分钟的1-10秒都会触发,

  其它占用符使用方法一样,输入范围可以看到参考前面"通配符解释"

 

递归规则定时器

  再看看另一种风格写定时器

var schedule = require(‘node-schedule‘);

function scheduleRecurrenceRule(){

    var rule = new schedule.RecurrenceRule();
    // rule.dayOfWeek = 2;
    // rule.month = 3;
    // rule.dayOfMonth = 1;
    // rule.hour = 1;
    // rule.minute = 42;
    rule.second = 0;
    
    schedule.scheduleJob(rule, function(){
       console.log(‘scheduleRecurrenceRule:‘ + new Date());
    });
   
}

scheduleRecurrenceRule();

  结果如下图:

技术分享

  从结果中可以看出,每分钟第60秒时就会触发,其它规则可以看我注释中的代码,当然,也可以组合使用,达到需求效果!

 

对象文本语法定时器

  直接看使用示例

var schedule = require(‘node-schedule‘);

function scheduleObjectLiteralSyntax(){

    //dayOfWeek
    //month
    //dayOfMonth
    //hour
    //minute
    //second

    schedule.scheduleJob({hour: 16, minute: 11, dayOfWeek: 1}, function(){
        console.log(‘scheduleObjectLiteralSyntax:‘ + new Date());
    });
   
}

scheduleObjectLiteralSyntax();

  结果如下图:

技术分享

  代码实现的是每周一的下午16:11分触发,其它组合可以根据我代码中的注释参数名自由组合

  

取消定时器

  示例如下,定时器对象的cancl方法即可

var schedule = require(‘node-schedule‘);

function scheduleCancel(){

    var counter = 1;
    var j = schedule.scheduleJob(‘* * * * * *‘, function(){
        
        console.log(‘定时器触发次数:‘ + counter);
        counter++;
        
    });

    setTimeout(function() {
        console.log(‘定时器取消‘)
        j.cancel();   
    }, 5000);
    
}

scheduleCancel();

  结果如下:

技术分享

写在之后

  定时器功能大部分需求都可以借助node-schedule完成了,用它在项目中使用效果也不错,各种需求可以满足^_^!

 

 

 

以上是关于优雅的使用node-schedule(上)的主要内容,如果未能解决你的问题,请参考以下文章

Nodejs学习笔记--- 定时任务(node-schedule)

飞书机器人通过 node-schedule 定时任务做一些打卡提醒和一些热搜新闻推荐

node 设置自动启用定时任务控件 node-schedule

NodeJS定时任务

node.js学习使用node.js定时发送邮件任务

nodeJS常用的定时执行任务的插件