Editor’s note: This article was last updated on 11 July 2023 to update the metrics of the Node.js schedulers in this list, as well as to include the popular Bottleneck job scheduler. For a closer look at some of these packages, check out these articles on Node.js scheduling with Agenda.js and Node-cron.
Job scheduling refers to the process of triggering a job, task, or function at a predetermined time or when certain events occur. Most job schedulers are based on cron, the time-based job scheduler in Unix-like systems.
There are several job schedulers for the Node.js runtime environment. Some of these schedulers run both in the browser and Node, while others run only in Node. These schedulers each have their own unique features, so choosing the best one for your use case can be a daunting and time-consuming process.
In this article, I will take you through the most popular job schedulers for Node.js to highlight their key features, differences, and similarities.
Jump ahead:
It is common to have routine and repetitive jobs on the server side. When working with complex systems, job scheduling is handy for repetitive jobs such as regularly testing service availability and generating reports on the overall system’s health. It ensures timely notification of developers and clients about downtime and service interruption.
Additionally, you may need to regularly process jobs such as database backups, routine system updates, sending email notifications, and data scraping on the server side. You can use schedulers to run such jobs at regular intervals.
Though most of the Node schedulers we will explore in this article are for scheduling jobs to run at specific intervals, others, such as Bottleneck, are for job scheduling and rate limiting. You can use Bottleneck to prioritize jobs and limit the duration for which each job runs and the number of concurrent jobs.
That way, you can schedule and allocate more time to high-priority jobs and push low-priority jobs to the back of the queue resulting in optimum resource utilization.
In this section, we will compare the most popular schedulers in the Node ecosystem so that you can easily choose one that meets your needs. In addition to the key features and functionalities offered by each package, I will highlight useful metrics and information such as bundle size, GitHub stars, release count, npm download statistics, package licenses, dependencies, and maintenance, among others.
Some metrics, such as npm download counts, may be used as initial pointers towards packages to explore but shouldn’t be used to decide which package is better than another. According to npm, the download statistics are naive by design; the reported number of downloads far exceeds the number of actual package installations.
Similarly, GitHub stars are not any different from social media likes. They can be used as a proxy indicator for the popularity of a package but they don’t tell you much about the quality, stability, or maturity of the project.
On the other hand, metrics such as release count, bundle size, dependencies, commit history, and age of the package, when looked at collectively, can give you some confidence about the package. You can then roll up your sleeves and dig into the actual codebase to ascertain the code quality yourself.
The Node.js job schedulers that we will explore include Agenda, Node-schedule, Node-cron, Bree, Cron, Bull, and Bottleneck.
Agenda is an MIT-licensed scheduler for Node. It was first published ten years ago and it is currently in version 5.0.0. It has 8.9k stars on GitHub and 96,234 weekly downloads on npm. According to the GitHub commits graph, it is actively maintained. Given the number of releases, Agenda is a very mature package.
Agenda offers functionality for flexibly scheduling jobs using both cron and a more human-readable syntax. You will need to have a working MongoDB database to use Agenda as a job scheduler.
If you are not interested in downloading and installing MongoDB in your system, you can get a taste of this scheduler by creating a Docker container or using MongoDB’s cloud database Atlas. If you are not interested in using MongoDB, then this package is probably not a good option for you.
In the code snippet below, I have defined a simple job that logs to the console every minute:
const Agenda = require("agenda"); require("dotenv").config(); const databaseUri = `mongodb+srv://mawa:${process.env.PASSWORD}@test.nq88g.mongodb.net/jobs?retryWrites=true&w=majority`; const agenda = new Agenda({ db: { address: databaseUri, collection: "test-jobs" }, }); agenda.define("log", async (job) => { const { name } = job.attrs.data; console.log(`Hello ${name}! ${new Date().toLocaleTimeString()}`); }); (async function () { await agenda.start(); await agenda.every("one minute", "log", { name: "John Doe" }); })();
Agenda is a feature-rich scheduler. It offers promise-based APIs, job scheduling with configurable priority and concurrency, persistence of job results, and much more, which you can read about in their documentation.
Despite the rich features offered by Agenda, it also comes with multiple dependencies. The image below shows some of its first, second, and third level dependencies:
The publish bundle size of Agenda is 393KB and the install size is 25.0MB according to packagephobia:
Node-schedule is another open source, MIT-licensed package for scheduling jobs in Node. It is the third most popular Node scheduler considering the number of downloads on npm. With 45 versions released up until the current version 2.1.1, Node-schedule has had plenty of time to adapt and improve over the years.
Node-schedule is mainly for time-based rather than interval-based scheduling, though you can also use it flexibly. With Node-schedule, you can easily schedule a job to run at specific dates with an optional recurrence configuration.
Node-schedule offers you the flexibility of scheduling jobs using cron-style scheduling and date-based scheduling. With cron-style scheduling, you can pass a cron expression to specify when the job is triggered and the expression is parsed using cron-parser:
const nodeSchedule = require('node-schedule'); const job = nodeSchedule.scheduleJob('* * * * *', function(){ console.log('Job has been triggered at: ', new Date.toLocaleTimeString()); })
On the other hand, with date-based scheduling, you can pass an actual JavaScript date object to specify the exact date when a job is to be executed. It also provides an option to bind current data for use in the future, as illustrated in the code snippet below:
const nodeSchedule = require('node-schedule'); const birthDay = new Date(2023, 11, 21, 5, 30, 0); // Bae's birthday let birthDayGift = 🎁; const job = nodeSchedule.scheduleJob(birthDay, function(gift){ console.log('Happy Birthday Babe ', new Date.toLocaleDateString()); console.log(gift); }.bind(null, birthDayGift)); birthDayGift = null;
One limitation of Node-schedule, like most Node schedulers, is that the job will be triggered only when the script is running. If you are seeking to schedule a job that will persist even when the script is not running, consider using cron. Similarly, it is not a good option if you want to persist the job between restarts.
Node-schedule has three first-level dependencies, which you can see in the dependency graph below:
According to Packagephobia, Node-schedule has a publish size of 34.1KB and an install size of 4.00MB:
Node-cron is an open source, ISC-licensed package. First published seven years ago, Node-cron is another mature, stable, and battle-tested Node scheduler.
It uses the GNU crontab expression for job scheduling. You can schedule Node-cron to trigger the job at a specific time or at intervals of time:
const nodeCron = require('node-cron'); nodeCron.schedule('* * * * * *', () => { // This job will run every second console.log(new Date().toLocaleTimeString()); })
Apart from the schedule
method for scheduling a job, Node-cron also exposes methods for validating a cron expression, starting, stopping, or destroying a scheduled job. Though Node-cron will serve your needs if you are scheduling a simple job in Node or on the server-side, it won’t be of much use if you need a scheduler that runs in the browser or with support for worker threads.
Node-cron has only one dependency. The image below shows its dependency tree:
Node-cron has a publish and install size of 66.8KB and 180KB respectively according to Packagephobia.
Bree is another MIT-licensed JavaScript job scheduler. It runs both in Node and in the browser. This package is relatively young compared to other schedulers mentioned above. It was first published approximately two years ago and about 95 versions have been released since then. The current version is 9.1.3 and has about 20,773 weekly downloads on npm.
However, despite being the youngest, Bree offers more features than most of the other schedulers. According to the documentation, Bree uses worker threads in Node and web workers in the browser to spawn sandboxed processes. It provides support for concurrency, throttling, long-running jobs, and much more. Check out the documentation for a full list of features.
The code snippet below shows how you can use Bree to schedule a simple job. scrape-data.js
, backup-database.js
, and send-email.js
contain functions declared in the jobs folder at the root of the project directory:
const Bree = require('bree'); const jobs = [ { name: 'scrape-data', interval: 'every 1 minute'}, { name: 'backup-database', timeout: 'at 12:00 am'}, { name: 'send-email', timeout: '1m', interval: '5m'} ]; const bree = new Bree({ jobs }); bree.start();
This package has 10 first-level dependencies as illustrated in the dependency tree below:
The publish size of Bree is 88.1KB and the install size is 4.88MB according to Packagephobia:
Similarly, if you want to use Bree on the client side, its minified and gzipped bundle size is 44.9KB according to Bundlephobia:
This MIT-licensed Node scheduler should not be confused with Node-cron — be careful! Both Cron and Node-cron have the same repository name, node-cron
, on GitHub. By searching on GitHub, you might think one is a fork of the other.
This package was first published twelve years ago, and there have since been 43 versions published. Cron is currently in version 2.3.1 and has 1,186,534 weekly downloads on npm, the second highest download count among the Node schedulers highlighted here.
You can use crontab syntax, '* * * * * *'
, to schedule when a job will be triggered. The expression extends the Unix pattern by adding a seconds option. If you don’t pass the seconds option, it defaults to zero.
Cron also offers the flexibility of passing a date object instead of the cron expression. Cron is very similar to Node-cron in terms of functionality, as you can see here:
const { CronJob } = require('cron'); const job = new CronJob('* * * * * *', function() { console.log('This job is triggered each second!'); }); job.start();
Cron has one dependency and 1,812 dependents on npm. The diagram below shows its dependency tree.
It has a publish and install size of 45.1KB and 3.88MB, respectively, according to Packagephobia. Cron’s publish size is 11KB higher than Node-schedule. Node-schedule has the lowest publish size among the schedulers highlighted in this article:
Bull is a Redis-based queue system for Node that requires a running Redis server. If you don’t want to use Redis, you will have to use a different scheduler. There are Redis hosting services that work well with Bull if you are not interested in installing it in your system.
Bull boasts the fastest and the most reliable Redis-based queue system for Node runtime environments. This MIT-licensed package published ten years ago and has about 526,856 weekly downloads on npm. The current version is 4.10.4, indicating its maturity and stability.
Bull offers features such as cron syntax-based job scheduling, rate-limiting of jobs, concurrency, running multiple jobs per queue, retries, and job priority, among others. For a full list of features, check out the documentation.
The trivial example below will log “Hello World!” to the console after five seconds with the default Redis configuration:
const Queue = require('bull'); const helloWorldQueue = new Queue('hello-world-queue'); const data = { someData: 'Hello World!' }; const options = { delay: 5000, attempts: 3 }; helloWorldQueue.add(data, options); helloWorldQueue.process((job) => { console.log(job.data.someData); });
This package has eight first-level dependencies. You can check the subsequent level dependencies in the dependency graph below:
Bull has a publish size of 229KB and an install size of 12.8MB according to Packagephobia:
Bottleneck is another lightweight, zero-dependency, MIT-licensed, task scheduler and rate limiter. It is isomorphic, which means that it runs both in Node.js and in the browser.
Bottleneck is different from the other schedulers highlighted above because it doesn’t schedule tasks to run at specific times or in certain intervals of time. Instead, it builds a queue of jobs and executes them as soon as possible, in the order in which they were received. You can configure the number of concurrent jobs and the waiting time between job starts. It also gives you the option to prioritize some jobs over others:
const Bottleneck = require("bottleneck"); const limiter = new Bottleneck({ mmaxConcurrent: 1, minTime: 1000, }); limiter .schedule(() => task()) .then((result) => { /* handle result */ });
Unlike most of the other Node.js schedulers highlighted here, Bottleneck has zero dependencies. The image below shows its dependency graph:
Bottleneck is an excellent package to limit the rate of API requests or concurrency on the client and server side. It has 2,014,469 weekly downloads on npm, the highest among the schedulers highlighted in this article. It has a publish and install size of 615KB according to Packagephobia:
To derive maximum benefit from this package, check out the Bottleneck documentation to learn more about its features and best practices while using it.
If you intend to use Bottleneck in the browser, it has 13.8KB of gzipped and minified bundle size according to Bundlephobia:
Some of the metrics and information highlighted in the previous sub-sections have been summarized in the table below:
Agenda | Node-cron | Node-schedule | Bree | Cron | Bull | Bottleneck | |
---|---|---|---|---|---|---|---|
Install bundle size | 25.0MB | 180KB | 4.00MB | 4.88MB | 3.88MB | 12.8MB | 615KB |
GitHub stars | 8.9k | 2.7k | 8.8k | 2.7k | 7.9k | 14.2k | 1.5k |
Versions released | 88 | 21 | 45 | 95 | 43 | 171 | 79 |
Publish bundle size | 393KB | 66.8KB | 34.1KB | 88.1KB | 45.1KB | 229KB | 615KB |
Minified + gzipped bundle size | 352.6KB | 5.7KB | 29.2KB | 44.9KB | 25.1KB | 122.4KB | 13.8KB |
Weekly downloads | 96,271 | 455,438 | 1,111,061 | 21,642 | 1,252,014 | 533,883 | 2,014,469 |
Documentation | Good | Good | Good | Good | Good | Good | Good |
License | MIT | ISC | MIT | MIT | MIT | MIT | MIT |
There are several job schedulers to pick from in the Node package ecosystem. Most of the packages have functionality that will meet your basic job scheduling needs, and your choice of a scheduler will largely depend on your use case. All the packages we have looked at above are free, open source, and well documented.
If you intend to do simple job scheduling on the server side, you might want to explore Cron, Node-cron, and Node-schedule because they are simple, mature, popular, stable, and have friendly licenses. I would recommend using one of the three for simple jobs.
Agenda and Bull also offer simple job scheduling but with additional features and support for database persistence. You wouldn’t want all those bundles and dependencies in your application unless you are interested in using the additional features they offer.
On the other hand, if you are looking for a scheduler with extended functionalities such as support for worker threads or ability to run both in the browser and in Node (in addition to simple job scheduling), then Bree might be the best option for you.
Is there anything that I missed? Let me know in the comment section below.
Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third-party services are successful, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.
LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. Start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowSimplify component interaction and dynamic theming in Vue 3 with defineExpose and for better control and flexibility.
Explore how to integrate TypeScript into a Node.js and Express application, leveraging ts-node, nodemon, and TypeScript path aliases.
es-toolkit is a lightweight, efficient JavaScript utility library, ideal as a modern Lodash alternative for smaller bundles.
The use cases for the ResizeObserver API may not be immediately obvious, so let’s take a look at a few practical examples.
7 Replies to "Comparing the best Node.js schedulers"
Plain Node.js job scheduling is not that convenient and reliable as OS scheduler solutions (`crontab` or `schtasks`). For that reason https://github.com/atmajs/cronbee combines both worlds – managing the jobs is done via CLI or API, and running them is left to the OS.
Node-cron fires multiple times sometimes, anyone knows how to solve that?
in my case use pm2 with cluster instance make multiple fires multiple times
Please check your number of instance. Because in every instance CRON gets executed, specially when you are running from server.
What do you think about croner?
https://github.com/Hexagon/croner
Which tool outputs my packages dependency graph?
Why isn’t https://github.com/hexagon/croner included?