A guide to Deno.cron
Tags: deno, webdev
Published: 2024-05-09 14:32:51

**Written by [Rosario De Chiara](https://blog.logrocket.com/author/rosariodechiara/)✏️**

In the UNIX world, one of the oldest and most reliable mechanisms available since the late 70s has been the [`cron` command](https://en.wikipedia.org/wiki/Cron). `cron` allows you to schedule the execution of tasks by specifying a simple (and sometimes cryptic) string to state when and how frequently a given command should be executed. 

In the heyday of UNIX, these strings, for every specific command, were collected in the `crontab` file. Now, even within a vastly different technological framework, the `cron` mechanism is still relevant. In this article, we will see how to use the `cron` package in Deno. The code we present here is available in this [GitHub repository](https://github.com/rosdec/denocron). 

We will assume that you have never used Deno before, so the first step is to install it. It is just a matter of executing the installation shell script; for the proper way of doing so, follow the [official documentation](https://docs.deno.com/runtime/manual#install-deno). To get acquainted with Deno, check out “[Getting started with Deno and Fresh](https://blog.logrocket.com/getting-started-deno-fresh/).”

## The `cron` string

The `crontab` string used by `Deno.cron` follows a specific format consisting of five fields, each representing different time parameters for scheduling tasks. The basic format includes a minute, hour, day of the month, month, and day of the week field:

![Scheduling Parameters](https://blog.logrocket.com/wp-content/uploads/2024/04/scheduling-parameters.png)

These fields are separated by one space, and numbers or special characters can be used to specify time intervals or specific points in time. This format allows users to define precise and recurring schedules for automated tasks. 

Here’s a simple example of a schedule using `Deno.cron`:

```rust
30 2 * * 1-5
30 2 * * 1-5
```

In this example, `30` specifies the minute when the task will be executed (in this case, 30 minutes past the hour). `2` represents the hour of the day when the task will be executed (2 AM). `*` stands for "every day of the month," meaning the task will occur on any day of the month. The next `*` indicates "every month," implying the task is scheduled for every month. And `1-5` refers to "Monday to Friday" for the day of the week, indicating that the task will run from Monday to Friday. 

In summary, this `crontab` string schedules a task to run at 2:30 AM from Monday to Friday. The good news is that the general idea of the `crontab` string from the late 70s has been translated to modern approaches, and it is possible to define a string by using a more expressive JavaScript object. We’ll explore this in the next section. 

The Cron library is available in Deno (v1.43 at the time of writing) but is tagged as unstable. This means that when you want to execute the code in the next examples, you must use this to explicitly enable the use of the (still) unstable `cron` API:

```typescript
   $ deno run --unstable-cron server.ts
```

The first example is pretty simple:

```rust
Deno.cron("task1", "* * * * *",
    () => console.log("This will print every ONE minute"))

Deno.cron("task2", { minute: { every: 2 } },
    () => console.log("This will print every TWO minutes"))
```

In the code, we defined two tasks with different schedules: the first one ( `'task1'`) is scheduled to run every minute because of the `crontab` string `"* * * * *"`. The second activity, named `task2`, is scheduled every two minutes by the JSON object:

```javascript
{
        minute: {
            every: 2
        }
    }      
```

You can probably agree that this is much more expressive than the `crontab` string. 

As expected, the execution prints the message from `task1` every minute, and the message from `task2` every two executions of `task1`:

```rust
    $ deno run --unstable-cron .\example1.ts
    This will print every ONE minute
    This will print every TWO minutes
    This will print every ONE minute
    This will print every ONE minute
    This will print every TWO minutes
```

In the first example, we saw how to schedule two pretty simple tasks. However, this mechanism has a limitation: the execution of the function associated with a task cannot overlap the execution of another task. If this is the case, Deno will skip the execution of the second task. 

This mechanism is important to understand because if the time required to complete a task's associated function is uncertain, the order in which tasks are executed will also be unpredictable because the execution cannot overlap and if a function is in execution, it blocks the execution of every other function. In the `example02.ts` file, here is a basic example to show this effect:

```javascript
import { sleep } from "https://deno.land/x/sleep@v1.3.0/mod.ts"
import { time } from "https://denopkg.com/burhanahmeed/time.ts@v2.0.1/mod.ts";

Deno.cron("task1", { minute: { every: 1 } },
    () => {
        console.log('Time now UTC: ', time().t)
    })

Deno.cron("task2", { minute: { every: 2 } },
    () => {
        console.log("Sleeping 3 minutes")
        sleep(3 * 60)
        console.log("Done")
    })
```

In this example, we will use two new Deno packages: `time`, to obtain the current time and `sleep`, to let a task waste some time in order to overlap the execution of the two tasks. 

In the following image, you can see an execution and you may notice how the scheduled task that prints the time (`task1` in the source) skipped its execution: there is a jump from minute 26 to minute 31\. This is more than the three minutes we let `task2` "sleep" and this clarifies how unreliable the scheduling of tasks whose executions may overlap can be:

```rust
$ deno run --unstable-cron .\example2.ts
Time now UTC:  2024-03-17T16:24:00.009Z
Time now UTC:  2024-03-17T16:25:00.001Z
Sleeping 3 minutes
Done
Time now UTC:  2024-03-17T16:26:00.010Z
Time now UTC:  2024-03-17T16:31:30.309Z
Sleeping 3 minutes
Done
Time now UTC:  2024-03-17T16:32:00.007Z
Sleeping 3 minutes
Done
```

The last example is useful in handling the failure of a recurring task. The general idea is to schedule a task in the usual way, as we have seen above, together with a particular schedule, named `backoffSchedule`, to be used in case of an error. 

The situation intended to be modeled here is as follows: schedule an invocation to a service, at a given pace. For our example, we’ll use five minutes. In case the invocation throws an error, keep retrying the invocation with a backoff time, so as to not clog the service with too many attempts:

```javascript
import { time } from "https://denopkg.com/burhanahmeed/time.ts@v2.0.1/mod.ts";

Deno.cron("task1", { minute: { every: 1 }}, {
    backoffSchedule: [1000, 5000, 10000],
}, () => {
    console.log('Time now UTC: ', time().t)

    throw new Error();
});
```

Unsuccessful cron executions are automatically attempted again according to a default retry strategy. To define this strategy, you can utilize the `backoffSchedule` attribute to specify an array of time intervals (in milliseconds) to wait before reattempting the function call. 

In the example above, we define a backoff schedule of one second, five seconds, and 10 seconds, and, to show how the backoff mechanism works, the function associated with `task1` will simply throw an error:

```rust
Time now UTC:  2024-03-17T18:40:00.009Z
Exception in cron handler task1 Error
    at file:///example3.ts:8:11
    at ext:deno_cron/01_cron.ts:101:24
    at eventLoopTick (ext:core/01_core.js:169:7)
Time now UTC:  2024-03-17T18:40:01.029Z
Exception in cron handler task1 Error
    at file:///example3.ts:8:11
    at ext:deno_cron/01_cron.ts:101:24
    at eventLoopTick (ext:core/01_core.js:169:7)
Time now UTC:  2024-03-17T18:40:06.037Z
Exception in cron handler task1 Error
    at file:///example3.ts:8:11
    at ext:deno_cron/01_cron.ts:101:24
    at eventLoopTick (ext:core/01_core.js:169:7)
Time now UTC:  2024-03-17T18:40:16.050Z
Exception in cron handler task1 Error
    at file:///example3.ts:8:11
    at ext:deno_cron/01_cron.ts:101:24
    at eventLoopTick (ext:core/01_core.js:169:7)
```

In the execution, you can notice that, after the first thrown exception (time `18:40:00`), we can see another exception after one second (time 18:40:01), five seconds (time 18:40:06), and 10 seconds (time 18:40:16).

## Running in the cloud

The real use of `Deno.cron` is on the server side of your application, to re-calculate data in a table, retry API calls, update dashboards, and so on. In the Deno ecosystem, this is done by leveraging [Deno Deploy](https://docs.deno.com/deploy/manual/), which is a globally distributed platform for serverless JavaScript applications. 

The implementation of `Deno.cron` works slightly differently on Deno Deploy. In particular, Deno Deploy assures that cron tasks are executed at least once per each scheduled time interval. This generally means that your cron handler will be invoked once per scheduled time but, in the presence of failures, the handler may be invoked multiple times for the same scheduled time.

## Conclusion

In summary, this article introduced the concept of scheduling tasks using `Deno.cron`, covering examples of various scheduling formats. We saw how important it is to account for non-deterministic task execution due to potentially overlapping functions, and offered practical examples for `Deno.cron`’s capabilities in handling scheduled tasks.

---

##Get set up with LogRocket's modern error tracking in minutes:

1. Visit https://logrocket.com/signup/ to get an app ID.
2. Install LogRocket via NPM or script tag. `LogRocket.init()` must be called client-side, not server-side.

NPM:
```bash
$ npm i --save logrocket 

// Code:

import LogRocket from 'logrocket'; 
LogRocket.init('app/id');
```
Script Tag:
```javascript
Add to your HTML:

<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>
```

3.(Optional) Install plugins for deeper integrations with your stack:
*   Redux middleware
*   ngrx middleware
*   Vuex plugin

[Get started now](https://lp.logrocket.com/blg/signup)