Node Scheduleで定期処理を行う

プロジェクトでnode-scheduleライブラリを使って定期処理を行いました。
この記事は自分用メモです。

# Node Scheduleの概要
Node Scheduleはインターバルベースではなく(interval-base),タイムベース(time-base)で定期処理を行います。

例えば、2021年11月８日にあるプロセス（メソッド）を実行したい場合、適切なパッケージだと思います。

Node Scheduleはcron-style schedulingです。

```
*    *    *    *    *    *
┬    ┬    ┬    ┬    ┬    ┬
│    │    │    │    │    │
│    │    │    │    │    └ 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)

```

# Node Scheduleの書式

Node Scheduleの詳しいドキュメントを読みたい場合、以下に２つのURLが貼られています。

-   [npmjs/node-schedule](https://www.npmjs.com/package/node-schedule)

-   [Repository](https://github.com/node-schedule/node-schedule)

# node-schedule パッケージのインストール

最初に、node-scheduleパッケージをインストールします。

```
$ npm i node-schedule
```

# node-scheduleパッケージの試し

### 1. Cron format

cron フォマットの例を述べます。秒数が30になる時、ジョブ(job)は実行されます。
(cronは[Node.jsで定期実行メモ](https://qiita.com/n0bisuke/items/66abf6ca1c12f495aa04)を参照します）

```js
/// test.js
const schedule = require('node-schedule');

const job = schedule.scheduleJob('30 * * * * *', function(){
    var date = new Date();
    console.log('hh:mm:30に実行します'+date);
  });
```

出力は以下のようになります。

```
$ node test.js 
hh:mm:30に実行しますMon Nov 08 2021 16:12:30 GMT+0900 (Japan Standard Time)
hh:mm:30に実行しますMon Nov 08 2021 16:13:30 GMT+0900 (Japan Standard Time)
hh:mm:30に実行しますMon Nov 08 2021 16:14:30 GMT+0900 (Japan Standard Time)
hh:mm:30に実行しますMon Nov 08 2021 16:15:30 GMT+0900 (Japan Standard Time)
```

メソッドを外に宣言する場合は以下のようになります。出力は同じです。

```js
/// test.js
const schedule = require('node-schedule');

var process = function() {
    var date = new Date();
    console.log('hh:mm:30に実行します'+date);
}

const job = schedule.scheduleJob('30 * * * * *', process);
```

### 2. デートベース・スケジューリング

決められた時間にジョブを実行したい時、デートベーススケジューリング(Date-based scheduling)を使います。

```js
/// test.js
const schedule = require('node-schedule');

var process = function() {
    var date = new Date()
    console.log("実行されています。" + date)
}

var scheduleTime = new Date("November 08, 2021 16:35:00")

const job = schedule.scheduleJob(scheduleTime, process);
```

出力は以下のようになります。ジョブが終わったら、プログラムも終了します。

```
$ node test.js 
実行されています。Mon Nov 08 2021 16:35:00 GMT+0900 (Japan Standard Time)
$ 
```

### 3. 複数のジョブ
プロジェクトでは複数のジョブをスケジュールしました。以下のようなdummy例を述べます。スケジュールリストを作成し、各要素は実行時間とコンテンツを持っています。

ここはscheduleJobsで登録したジョブを確認できます。

```js
/// test.js
const schedule = require('node-schedule');

var process = function(job) {
    var time = job['processTime'];
    var content = job['processContent'];

    var date = new Date()
    var curr = new Date()
    date.setTime(time)
    console.log(curr);
    console.log(content + " 実行されています。" + date)
}

var time1 = new Date("November 08, 2021 18:07:00").getTime()
var time2 = new Date("November 08, 2021 18:09:00").getTime()

const scheduleList = [
    {
        processTime: time1,
        processContent: "プロセス1",

    },
    {
        processTime: time2,
        processContent: "プロセス2",
    }
]

// ジョブの登録
for (var i = 0; i < scheduleList.length; i++) {
    var job =  scheduleList[i];
    var processTime = job['processTime'];
    var processContent = job['processContent'];

    //scheduleJobのパラメーター: ジョブ名, 実行時間, 実行メソッド
    schedule.scheduleJob(processContent, new Date().setTime(processTime),function(job){
        process(job);
        console.log("現時点のスケジュール")
        console.log(schedule.scheduledJobs)
    }.bind(null,job))
}

// 登録したジョブを確認する
console.log("登録したジョブを確認する")
console.log(schedule.scheduledJobs)


```

このコードの出力はこのようになります。

```
$ node test.js 
登録したジョブを確認する
{
  'プロセス1': Job {
    pendingInvocations: [ [Invocation] ],
    job: [Function: bound ],
    callback: false,
    name: 'プロセス1',
    trackInvocation: [Function (anonymous)],
    stopTrackingInvocation: [Function (anonymous)],
    triggeredJobs: [Function (anonymous)],
    setTriggeredJobs: [Function (anonymous)],
    deleteFromSchedule: [Function (anonymous)],
    cancel: [Function (anonymous)],
    cancelNext: [Function (anonymous)],
    reschedule: [Function (anonymous)],
    nextInvocation: [Function (anonymous)],
    isOneTimeJob: true
  },
  'プロセス2': Job {
    pendingInvocations: [ [Invocation] ],
    job: [Function: bound ],
    callback: false,
    name: 'プロセス2',
    trackInvocation: [Function (anonymous)],
    stopTrackingInvocation: [Function (anonymous)],
    triggeredJobs: [Function (anonymous)],
    setTriggeredJobs: [Function (anonymous)],
    deleteFromSchedule: [Function (anonymous)],
    cancel: [Function (anonymous)],
    cancelNext: [Function (anonymous)],
    reschedule: [Function (anonymous)],
    nextInvocation: [Function (anonymous)],
    isOneTimeJob: true
  }
}
2021-11-08T09:07:00.005Z
プロセス1 実行されています。Mon Nov 08 2021 18:07:00 GMT+0900 (Japan Standard Time)
現時点のスケジュール
{
  'プロセス1': Job {
    pendingInvocations: [],
    job: [Function: bound ],
    callback: false,
    name: 'プロセス1',
    trackInvocation: [Function (anonymous)],
    stopTrackingInvocation: [Function (anonymous)],
    triggeredJobs: [Function (anonymous)],
    setTriggeredJobs: [Function (anonymous)],
    deleteFromSchedule: [Function (anonymous)],
    cancel: [Function (anonymous)],
    cancelNext: [Function (anonymous)],
    reschedule: [Function (anonymous)],
    nextInvocation: [Function (anonymous)],
    isOneTimeJob: true
  },
  'プロセス2': Job {
    pendingInvocations: [ [Invocation] ],
    job: [Function: bound ],
    callback: false,
    name: 'プロセス2',
    trackInvocation: [Function (anonymous)],
    stopTrackingInvocation: [Function (anonymous)],
    triggeredJobs: [Function (anonymous)],
    setTriggeredJobs: [Function (anonymous)],
    deleteFromSchedule: [Function (anonymous)],
    cancel: [Function (anonymous)],
    cancelNext: [Function (anonymous)],
    reschedule: [Function (anonymous)],
    nextInvocation: [Function (anonymous)],
    isOneTimeJob: true
  }
}
2021-11-08T09:09:00.004Z
プロセス2 実行されています。Mon Nov 08 2021 18:09:00 GMT+0900 (Japan Standard Time)
現時点のスケジュール
{
  'プロセス2': Job {
    pendingInvocations: [],
    job: [Function: bound ],
    callback: false,
    name: 'プロセス2',
    trackInvocation: [Function (anonymous)],
    stopTrackingInvocation: [Function (anonymous)],
    triggeredJobs: [Function (anonymous)],
    setTriggeredJobs: [Function (anonymous)],
    deleteFromSchedule: [Function (anonymous)],
    cancel: [Function (anonymous)],
    cancelNext: [Function (anonymous)],
    reschedule: [Function (anonymous)],
    nextInvocation: [Function (anonymous)],
    isOneTimeJob: true
  }
}
```

以上の結果によると、プロセス２を実行する時、スケジュールにプロセス１が排除されることがわかります。

cronではないフォマットをスケジュールしたい場合はNode Scheduleが良いと思います。