agenda、cron、later、node-cron、node-schedule はすべて Node.js 環境で定期実行やスケジュールされたタスクを管理するためのライブラリです。これらのライブラリは、Cron 式による繰り返しジョブの設定や、特定日時での一回限りの実行といった基本機能を提供しますが、永続化、依存関係、柔軟なスケジュール記述、およびアーキテクチャ設計において大きく異なります。例えば、agenda は MongoDB を使ってジョブを永続化し、再起動後も状態を維持できる一方、cron や node-cron はメモリ上でのみ動作し、プロセス終了時にスケジュールが失われます。later は独自のスケジュール DSL を提供し、Cron 式以外の柔軟な指定が可能ですが、メンテナンスが停止されています。
Node.js で定期処理やスケジュール実行を行うには、いくつかの選択肢があります。それぞれのライブラリは「いつ」「何を」「どう実行するか」の設計思想が異なり、用途に応じて適切なものを選ぶ必要があります。この記事では、5つの主要なライブラリを実際のコードとともに比較し、現場で役立つ判断基準を示します。
最初に重要な注意点です。later は 公式に非推奨(deprecated) となっています。npm ページおよび GitHub リポジトリ(bunkat/later)は 2016 年以降更新されておらず、アーカイブ済みです。新規プロジェクトでは絶対に使用せず、代わりに cron や node-schedule を検討してください。
多くのライブラリは Unix 標準の Cron 式(例:'0 9 * * *' = 毎日9時)をサポートしています。以下に各ライブラリでの実装例を示します。
cronconst CronJob = require('cron').CronJob;
const job = new CronJob('0 9 * * *', () => {
console.log('毎日9時に実行');
}, null, true, 'Asia/Tokyo');
node-cronconst cron = require('node-cron');
cron.schedule('0 9 * * *', () => {
console.log('毎日9時に実行');
}, {
scheduled: true,
timezone: 'Asia/Tokyo'
});
node-scheduleconst schedule = require('node-schedule');
schedule.scheduleJob('0 9 * * *', () => {
console.log('毎日9時に実行');
});
agendaagenda は Cron 式を repeatEvery で使うことができますが、内部的には human-readable 文字列(例:'3 minutes')もサポートします。
const Agenda = require('agenda');
const agenda = new Agenda({ db: { address: 'mongodb://localhost/agenda' } });
agenda.define('daily task', (job) => {
console.log('毎日9時に実行');
});
(async () => {
await agenda.start();
await agenda.every('0 9 * * *', 'daily task');
})();
💡 注意:
laterは非推奨のため、コード例は省略します。
プロセスがクラッシュまたは再起動した際に、未完了のジョブを復元できるかどうかは、運用上の大きな違いです。
agenda:MongoDB で永続化agenda は MongoDB にジョブを保存するため、サーバー再起動後もスケジュールが維持されます。また、失敗したジョブのリトライやロック機構も備えています。
// MongoDB にジョブが保存される
await agenda.schedule('tomorrow at 9am', 'send email', { to: 'user@example.com' });
cron, node-cron, node-schedule):メモリ上のみこれらはすべて メモリ上でのみ スケジュールを管理します。プロセスが終了すると、登録済みのジョブはすべて失われます。
// node-cron の例:プロセス終了でスケジュール消失
const task = cron.schedule('*/5 * * * *', () => {
// 5分ごとの処理
});
// process.exit() で task は完全に消える
この特性から、cron/node-cron/node-schedule は「アプリケーション起動中にだけ有効な一時タスク」に適しています。一方、agenda は「システム全体で信頼性が求められるバッチ処理」に適しています。
Cron 式だけでは表現できないスケジュール(例:「毎月第2水曜日」)を扱いたい場合があります。
node-schedule:RecurrenceRulenode-schedule は RecurrenceRule オブジェクトで詳細なルールを定義できます。
const schedule = require('node-schedule');
const rule = new schedule.RecurrenceRule();
rule.dayOfWeek = 3; // 水曜日(0=日曜)
rule.hour = 9;
rule.minute = 0;
// ただし「第2水曜日」は直接指定不可 → 別途ロジックが必要
schedule.scheduleJob(rule, () => {
console.log('毎週水曜日9時');
});
実際には「第2水曜日」を正確に表現するには追加計算が必要ですが、
node-scheduleは Date オブジェクトやカスタムルールで柔軟に対応可能です。
later(非推奨):独自 DSLlater は 'every 2 weeks on Wednesday at 9:00' のような自然言語風の記述をサポートしていましたが、現在は使用できません。
cron、node-cron、agenda は基本的に Cron 式に依存しており、複雑なカスタムロジックは自前で実装する必要があります。
cron:コールバック中心。async/await と組み合わせにくい。node-cron:Promise 対応。async 関数を直接渡せる。node-schedule:コールバックだが、戻り値として Job オブジェクトを返し、動的制御が可能。agenda:Promise/async 対応。モダンな非同期処理に最適。// node-cron:async 対応
cron.schedule('* * * * *', async () => {
await fetchData();
});
// agenda:async 対応
agenda.define('fetch data', async (job) => {
await fetchData();
});
cron:job.start() / job.stop()node-cron:task.start() / task.stop()node-schedule:job.cancel()agenda:agenda.cancel() で条件に基づき削除可能// node-schedule:動的キャンセル
const job = schedule.scheduleJob('*/10 * * * *', () => {
console.log('10分ごと');
});
// ある条件で停止
if (needStop) job.cancel();
| ライブラリ | 外部依存 | 永続化 | 本番向き |
|---|---|---|---|
agenda | MongoDB 必須 | ✅ | ✅ |
cron | なし | ❌ | △(小規模) |
node-cron | なし | ❌ | △(小規模) |
node-schedule | なし | ❌ | △(中規模) |
later | なし(非推奨) | ❌ | ❌ |
node-cron が軽量で使いやすい。node-schedule がバランス良い。agenda 一択(MongoDB 導入が前提)。agenda(MongoDB 使用可なら)node-cron(Promise 対応で現代的)node-schedulecron(ただし新規プロジェクトでは node-cron 推奨)later は絶対に使わない最終的に、あなたのアプリケーションが「プロセス寿命を超えてジョブを維持する必要があるか?」という一点で、agenda かそれ以外かが決まります。その上で、API の使いやすさやスケジュールの柔軟性で微調整するのがベストプラクティスです。
node-schedule は Cron 式だけでなく、Date オブジェクトや RecurrenceRule による柔軟なスケジュール指定が可能です。たとえば「毎月第2火曜日」のような複雑なルールも表現できます。永続化機能はありませんが、スケジュールの動的変更やキャンセルが容易で、中規模アプリケーションでの一時的なタスク管理に適しています。
agenda はジョブの永続化とクラスタ対応が必要な本番環境向けに最適です。MongoDB をバックエンドとして使い、再起動後も未完了のジョブを復元できるため、信頼性が求められるシステム(例:メール配信、データ同期)に適しています。ただし、MongoDB への依存があるため、インフラ要件が増える点に注意が必要です。
cron はシンプルで軽量な Cron 実装を求めている場合に適しています。標準的な Cron 式をサポートし、コールバックベースの API で簡単に使えるため、小規模なスクリプトやプロトタイプ開発に向いています。ただし、ジョブの永続化や詳細な制御機能はなく、プロセス終了時にスケジュールが消える点に留意してください。
later は非推奨(deprecated)のパッケージであり、新規プロジェクトでの使用は避けてください。最後の更新は 2016 年以降なく、公式 GitHub リポジトリもアーカイブされています。代替として cron や node-schedule を検討すべきです。
node-cron は標準的な Cron 式を使い、Promise 対応や TypeScript サポートなど現代的な API を備えています。軽量で依存が少なく、シンプルな定期タスク(例:監視スクリプト、キャッシュ更新)に最適です。ただし、ジョブの永続化や高度なスケジュールロジックはサポートしていません。
Node Schedule is a flexible cron-like and not-cron-like job scheduler for Node.js. It allows you to schedule jobs (arbitrary functions) for execution at specific dates, with optional recurrence rules. It only uses a single timer at any given time (rather than reevaluating upcoming jobs every second/minute).
Node 6+ is supported.
Node Schedule is for time-based scheduling, not interval-based scheduling.
While you can easily bend it to your will, if you only want to do something like
"run this function every 5 minutes", toad-scheduler would be a better choice. But if you want to, say, "run this function at the :20
and :50 of every hour on the third Tuesday of every month," you'll find that
Node Schedule suits your needs better. Additionally, Node Schedule has Windows
support, unlike true cron, since the node runtime is now fully supported.
Note that Node Schedule is designed for in-process scheduling, i.e. scheduled jobs will only fire as long as your script is running, and the schedule will disappear when execution completes. If you need to schedule jobs that will persist even when your script isn't running, consider using actual cron.
In case you need durable jobs that persist across restarts and lock system compatible with multi-node deployments, try agenda or bree.
You can install using npm.
npm install node-schedule
Every scheduled job in Node Schedule is represented by a Job object. You can
create jobs manually, then execute the schedule() method to apply a schedule,
or use the convenience function scheduleJob() as demonstrated below.
Job objects are EventEmitters, and emit the following events:
run event after each execution.scheduled event each time they're scheduled to run.canceled event when an invocation is canceled before it's executed.canceled is the single-L American spelling.error event when a job invocation triggered by a schedule throws or returns
a rejected Promise.success event when a job invocation triggered by a schedule returns successfully or
returns a resolved Promise. In any case, the success event receives the value returned
by the callback or in case of a promise, the resolved value.(Both the scheduled and canceled events receive a JavaScript date object as
a parameter).
Note that jobs are scheduled the first time immediately, so if you create a job
using the scheduleJob() convenience method, you'll miss the first scheduled
event, but you can query the invocation manually (see below).
The cron format consists of:
* * * * * *
┬ ┬ ┬ ┬ ┬ ┬
│ │ │ │ │ │
│ │ │ │ │ └ 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)
Examples with the cron format:
const schedule = require('node-schedule');
const job = schedule.scheduleJob('42 * * * *', function(){
console.log('The answer to life, the universe, and everything!');
});
Execute a cron job when the minute is 42 (e.g. 19:42, 20:42, etc.).
And:
const job = schedule.scheduleJob('0 17 ? * 0,4-6', function(){
console.log('Today is recognized by Rebecca Black!');
});
Execute a cron job every 5 Minutes = */5 * * * *
You can also get when it is scheduled to run for every invocation of the job:
const job = schedule.scheduleJob('0 1 * * *', function(fireDate){
console.log('This job was supposed to run at ' + fireDate + ', but actually ran at ' + new Date());
});
This is useful when you need to check if there is a delay of the job invocation when the system is busy, or save a record of all invocations of a job for audit purpose.
Currently, W (nearest weekday) and L (last day of month/week) are not supported.
Most other features supported by popular cron implementations should work just fine,
including # (nth weekday of the month).
cron-parser is used to parse crontab instructions.
Say you very specifically want a function to execute at 5:30am on December 21, 2012. Remember - in JavaScript - 0 - January, 11 - December.
const schedule = require('node-schedule');
const date = new Date(2012, 11, 21, 5, 30, 0);
const job = schedule.scheduleJob(date, function(){
console.log('The world is going to end today.');
});
To use current data in the future you can use binding:
const schedule = require('node-schedule');
const date = new Date(2012, 11, 21, 5, 30, 0);
const x = 'Tada!';
const job = schedule.scheduleJob(date, function(y){
console.log(y);
}.bind(null,x));
x = 'Changing Data';
This will log 'Tada!' when the scheduled Job runs, rather than 'Changing Data', which x changes to immediately after scheduling.
You can build recurrence rules to specify when a job should recur. For instance, consider this rule, which executes the function every hour at 42 minutes after the hour:
const schedule = require('node-schedule');
const rule = new schedule.RecurrenceRule();
rule.minute = 42;
const job = schedule.scheduleJob(rule, function(){
console.log('The answer to life, the universe, and everything!');
});
You can also use arrays to specify a list of acceptable values, and the Range
object to specify a range of start and end values, with an optional step parameter.
For instance, this will print a message on Thursday, Friday, Saturday, and Sunday at 5pm:
const rule = new schedule.RecurrenceRule();
rule.dayOfWeek = [0, new schedule.Range(4, 6)];
rule.hour = 17;
rule.minute = 0;
const job = schedule.scheduleJob(rule, function(){
console.log('Today is recognized by Rebecca Black!');
});
Timezones are also supported. Here is an example of executing at the start of every day in the UTC timezone.
const rule = new schedule.RecurrenceRule();
rule.hour = 0;
rule.minute = 0;
rule.tz = 'Etc/UTC';
const job = schedule.scheduleJob(rule, function(){
console.log('A new day has begun in the UTC timezone!');
});
A list of acceptable tz (timezone) values can be found at https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
second (0-59)minute (0-59)hour (0-23)date (1-31)month (0-11)yeardayOfWeek (0-6) Starting with SundaytzNote: It's worth noting that the default value of a component of a recurrence rule is
null(except for second, which is 0 for familiarity with cron). If we did not explicitly setminuteto 0 above, the message would have instead been logged at 5:00pm, 5:01pm, 5:02pm, ..., 5:59pm. Probably not what you want.
To make things a little easier, an object literal syntax is also supported, like in this example which will log a message every Sunday at 2:30pm:
const job = schedule.scheduleJob({hour: 14, minute: 30, dayOfWeek: 0}, function(){
console.log('Time for tea!');
});
It will run after 5 seconds and stop after 10 seconds in this example. The ruledat supports the above.
const startTime = new Date(Date.now() + 5000);
const endTime = new Date(startTime.getTime() + 5000);
const job = schedule.scheduleJob({ start: startTime, end: endTime, rule: '*/1 * * * * *' }, function(){
console.log('Time for tea!');
});
You can shutdown jobs gracefully.
gracefulShutdown() will cancel all jobs and return Promise.
It will wait until all jobs are terminated.
schedule.gracefulShutdown();
You can also gracefully shutdown jobs when a system interrupt occurs.
process.on('SIGINT', function () {
schedule.gracefulShutdown()
.then(() => process.exit(0))
}
There are some function to get information for a Job and to handle the Job and Invocations.
You can invalidate any job with the cancel() method:
j.cancel();
All planned invocations will be canceled. When you set the parameter reschedule to true then the Job is newly scheduled afterwards.
This method invalidates the next planned invocation or the job. When you set the parameter reschedule to true then the Job is newly scheduled afterwards.
This method cancels all pending invocation and registers the Job completely new again using the given specification. Return true/false on success/failure.
This method returns a Date object for the planned next Invocation for this Job. If no invocation is planned the method returns null.
This module was originally developed by Matt Patenaude who eventually passed over maintainer's mantle over to Tejas Manohar.
Currently it is being maintained by Igor Savin and our amazing community.
We'd love to get your contributions. Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit.
Before jumping in, check out our Contributing page guide!
Copyright 2015 Matt Patenaude.
Licensed under the MIT License.