優(yōu)雅的使用node-schedule(上)

前言

在 Javascript 中退腥,有時候我們有定時事務(wù)的需求,自己借助setTimeout和setInterval來實現(xiàn)的化太過麻煩温兼,node-schedule是一個非常不錯的npm包,可以幫助我們快速的創(chuàng)建和管理定時事務(wù)。
本文主要介紹 node-schedule 的基礎(chǔ)用法比搭。

node-schedule介紹

安裝

npm install node-schedule

創(chuàng)建計劃

需要用到scheduleJob函數(shù),會返回一個Job實例對象:

function scheduleJob(name: string, rule: ..., callback: function): schedule.Job
  • name
    任務(wù)名南誊,當(dāng)你沒有指定時身诺,它將以時間戳作為名字:'<Anonymous Job 1 2023-04-20T10:23:28.966Z>'
  • rule:
    任務(wù)調(diào)度的規(guī)則,支持多種形式的rule:
    • string - Cron表達式
    • number
    • schedule.RecurrenceRule
    • Date
  • callback
    創(chuàng)建任務(wù)時的回調(diào)函數(shù)

可以通過scheduleJob(name, rule, callback)或者scheduleJob(rule, callback)創(chuàng)建計劃抄囚。

調(diào)度規(guī)則

基于Cron表達式[1]的規(guī)則

在 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時,建議寫完整幔托,可讀性比較好穴亏。

定時循環(huán)
cron表達式作為rule時,用來指定每當(dāng)某個/某些時刻觸發(fā)任務(wù)的調(diào)度執(zhí)行重挑,譬如:

  • 每秒執(zhí)行
    * 號代表的意思是嗓化,因此這條rule代表:全年的每天每秒都觸發(fā)
const rule = '* * * * * *';
const job = schedule.scheduleJob(rule,()=>{});
  • 每N秒執(zhí)行
    */1 [2]代表從頭開始,每1秒觸發(fā)一次
const rule = '*/1 * * * * *';
const job = schedule.scheduleJob(rule,()=>{});
  • 每個半點時觸發(fā)谬哀,在第30分鐘內(nèi)的每秒執(zhí)行
const rule = '* 30 * * * *';
const job = schedule.scheduleJob(rule,()=>{});
  • 每個半點15秒時觸發(fā)刺覆,在第30分鐘內(nèi)的每10秒執(zhí)行
const rule = '*/10 30 * * * *';
const job = schedule.scheduleJob(rule,()=>{});
  • 每個1秒,3秒和9秒執(zhí)行
const rule = '1,3,9 * * * * *';
const job = schedule.scheduleJob(rule,()=>{});
  • 每個星期日觸發(fā)史煎,全天每秒都執(zhí)行
    0和7都是星期日谦屑,1-6對應(yīng)周一到周六,此時必須把日域用 ? 覆蓋篇梭,如果日用 * 伦仍,意味著每天都觸發(fā),會覆蓋周日的限制[3]
const rule = '* * * ? * 0';
const job = schedule.scheduleJob(rule,()=>{});
  • 每月第4個星期日觸發(fā)很洋,全體每秒都執(zhí)行
    #N [4]在后面指定第幾個星期幾
const rule = '* * * ? * 0#4';
const job = schedule.scheduleJob(rule,()=>{});
  • 每周一早上的零點執(zhí)行
const rule = '0 0 0 ? * 1';
const job = schedule.scheduleJob(rule,()=>{});
  • 每0~10分內(nèi)充蓝,每分鐘的第0秒觸發(fā)
    0-10 [5]代表這個范圍,并且是閉區(qū)間
const rule = '0 0-10 * * * *';
const job = schedule.scheduleJob(rule,()=>{});
  • 每月的20日觸發(fā)
    此時必須把星期域用 ? 覆蓋,如果周幾用 * 谓苟,意味著每個周幾都觸發(fā)官脓,那就是每天,會覆蓋20日的限制
const rule = '* * * 20 * ?';
const job = schedule.scheduleJob(rule,()=>{});

注意

  • node-schedule中不支持cron的 LW 語法(最后和最近)
  • node-schedule中不支持Year涝焙,因此只有6個域卑笨,默認(rèn)每年
  • 創(chuàng)建了計劃后,到點就會觸發(fā)了仑撞,Job沒有run,start之類的方法
  • 到達觸發(fā)時間點時赤兴,node-schedule會以同樣的方式創(chuàng)建一個新Job

cron表達式很簡潔,不過可讀性也是比較低隧哮,每次看到都要思考一下桶良,我們來看一下別的rule寫法

基于Date的規(guī)則

假設(shè)您非常希望在一個精確到某一個時間點上的秒數(shù)的僅觸發(fā)一次的計劃,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的規(guī)則

可能有人還記得 rule 匹配的類型中number,其實它對應(yīng)的是時間戳榆鼠,如果接收到了number的rule纲爸,會被當(dāng)作時間戳來生成相應(yīng)的Date,接著通過基于Date的rule去創(chuàng)建計劃

基于RecurrenceRule的規(guī)則

如果你的任務(wù)是定時重復(fù)執(zhí)行的妆够,并且你希望有比cron更高的可讀性缩焦,你可以嘗試使用RecurrenceRule對象作為rule

  • 創(chuàng)建RecurrenceRule對象
let rule = new RecurrenceRule();

Recurrence的構(gòu)造函數(shù):

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

因此你也可以在構(gòu)造的時候就把參數(shù)傳進去,如果沒傳的责静,默認(rèn)是每

  • 設(shè)置重復(fù)時刻
    每個域可以賦的值和cron中基本類似袁滥,不過有一下幾點區(qū)別:
  • 除了tz,其他都必須是數(shù)字
  • dayOfWeek 范圍是 0~6灾螃,不再支持7作為Sunday
  • month 范圍是 0~11 而非1~12
  • 不能像cron那樣設(shè)置第幾個周五這樣子
  • 支持時區(qū)設(shè)置题翻,點擊查看所有可以作為tz的值
    如果同一個域下可以有多個值,則須要把所有的值放在一個數(shù)組中賦給該域:
//每秒觸發(fā)
rule.second = [1,2,3,... ...,60];

如果只有兩三個值那還好腰鬼,像上面這樣的60個值手都麻了嵌赠。如果是連續(xù)的值,node-schedule提供了一個Range函數(shù)用于創(chuàng)建連續(xù)的元素:

rule.second = [0,1,2,new schedule.Range(10,20)];  //必須作為[]的元素熄赡,本身不是一個數(shù)組

更方便的創(chuàng)建
你還可以直接使用鍵值式對象當(dāng)作RecurrenceRule作為rule:

const job = schedule.scheduleJob(
    {
        hour: 14,
        minute: 30,
        dayOfWeek: 0
    }, 
    function (){
        //...
    }
);
基于RecurrenceSpecDateRange的規(guī)則

源碼里面沒看到姜挺,可能已廢棄

基于RecurrenceSpecObjLit的規(guī)則

源碼里面沒看到,可能已廢棄

結(jié)束計劃

調(diào)用 Job 實例中的 cancel 方法即可結(jié)束計劃的運行

job.cancel();

任務(wù)內(nèi)容

  • 通常把任務(wù)的內(nèi)容寫在創(chuàng)建時的callback函數(shù)[6]內(nèi)
const job = schedule.scheduleJob("* * * * * *",()=>{
    console.log("任務(wù)進行中...");
});
  • 當(dāng)然你將任務(wù)內(nèi)容綁定在監(jiān)聽到Job運行完時觸發(fā)的函數(shù)內(nèi)也無傷大雅(狀態(tài)監(jiān)聽稍后講)
this.job.on("run",()=>{
    console.log("任務(wù)進行中...");
    console.log("任務(wù)結(jié)束");
});

不過這樣做的前提是彼硫,你不需要在任務(wù)內(nèi)容執(zhí)行完后返回一些相關(guān)的數(shù)據(jù)和信息炊豪。因此原則上規(guī)范的做法是凌箕,把任務(wù)的內(nèi)容寫在創(chuàng)建時的callback內(nèi)

狀態(tài)監(jiān)聽

總共有5個事件可以監(jiān)聽(其實創(chuàng)建也能算一個),不說什么了词渤,直接擺代碼:

//這個job被我存儲一個實例內(nèi)牵舱,所以是this.job
this.job = schedule.scheduleJob(this.rule,
//2.在執(zhí)行這里的回調(diào)
()=>{
    console.log("任務(wù)運行...");
    return "執(zhí)行了一件node-schedule任務(wù)"; //可以被監(jiān)聽success的回調(diào)函數(shù)捕捉
});

//1.先執(zhí)行 scheduled 回調(diào)
this.job.on("scheduled",()=>{
    console.log("任務(wù)被調(diào)度");
    //scheduled的回調(diào)函數(shù)中出的錯不會被監(jiān)聽error所捕獲
});

//3.再執(zhí)行 run 回調(diào)
this.job.on("run",()=>{
    console.log("任務(wù)結(jié)束");
});

//4.再執(zhí)行 success 回調(diào)
this.job.on("success",(data)=>{
    console.log(`任務(wù)成果: ${data}`);
    console.log("任務(wù)成功!\n");
});

//5.只監(jiān)聽任務(wù)創(chuàng)建回調(diào),run回調(diào)和success回調(diào)中產(chǎn)生的異常
this.job.on("error",(err)=>{
    console.log(`[error][${new Date().toLocaleString()}]${err.message}`);
});

//6.計劃被取消的那一刻執(zhí)行 canceled
this.job.on("canceled",()=>{
    console.log("計劃結(jié)束!");
})

腳注


  1. Cron表達式是一種被多個空格隔成多個域的字符串,每一個域由數(shù)字和特殊字符組成缺虐,代表一種含義芜壁,通常用于指定時間規(guī)則 ?

  2. S/NS 表示從該域最起始的位置S處觸發(fā)一次,接著每隔N觸發(fā)一次高氮,5/20慧妄,5觸發(fā),然后25剪芍,45... ?

  3. 因為日域和周幾域都是關(guān)于天的規(guī)則塞淹,因此會產(chǎn)生沖突,此時就需要用 ? 把其中一項的優(yōu)先級降低 ?

  4. #N 只能用于星期幾的那個域里面 ?

  5. -代表范圍紊浩,并且是閉區(qū)域 ?

  6. 這個函數(shù)可以傳入?yún)?shù),但說實在的疗锐,沒有必要 ?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末坊谁,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子滑臊,更是在濱河造成了極大的恐慌口芍,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件雇卷,死亡現(xiàn)場離奇詭異鬓椭,居然都是意外死亡,警方通過查閱死者的電腦和手機关划,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門小染,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人贮折,你說我怎么就攤上這事裤翩。” “怎么了调榄?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵踊赠,是天一觀的道長。 經(jīng)常有香客問我每庆,道長筐带,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任缤灵,我火速辦了婚禮伦籍,結(jié)果婚禮上蓝晒,老公的妹妹穿的比我還像新娘。我一直安慰自己鸽斟,他們只是感情好拔创,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著富蓄,像睡著了一般剩燥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上立倍,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天灭红,我揣著相機與錄音,去河邊找鬼口注。 笑死变擒,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的寝志。 我是一名探鬼主播娇斑,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼材部!你這毒婦竟也來了毫缆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤乐导,失蹤者是張志新(化名)和其女友劉穎苦丁,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體物臂,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡旺拉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了棵磷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蛾狗。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖仪媒,靈堂內(nèi)的尸體忽然破棺而出淘太,到底是詐尸還是另有隱情,我是刑警寧澤规丽,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布蒲牧,位于F島的核電站,受9級特大地震影響赌莺,放射性物質(zhì)發(fā)生泄漏冰抢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一艘狭、第九天 我趴在偏房一處隱蔽的房頂上張望挎扰。 院中可真熱鬧翠订,春花似錦、人聲如沸遵倦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽梧躺。三九已至似谁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間掠哥,已是汗流浹背巩踏。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留续搀,地道東北人塞琼。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像禁舷,于是被迫代替她去往敵國和親彪杉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容