-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCronDaemon.ts
88 lines (79 loc) · 2.33 KB
/
CronDaemon.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import { parse, Schedule } from "./parse";
import { next } from "./next";
// When delay is larger than `(2^31) - 1)` the delay will be set to 1.
const MAX_DELAY = 2147483647;
/**
* Calculate the next time to execute the job.
*/
export type CustomSchedule = (now: Date) => Date | undefined;
/**
* Execute a cron schedule. The Daemon is initialized in the "started" state.
* @param schedule Cron schedule string e.g. `0,30 9-17 * * MON-FRI` or custom function to calculate the next date.
* @param callback Function to be invoked on the schedule.
* @throws If the schedule string is not valid cron syntax.
*/
export class CronDaemon {
private readonly schedule: Schedule | CustomSchedule;
private readonly callback: (date: Date) => void;
private timeout?: ReturnType<typeof setTimeout>;
constructor(
schedule: string | CustomSchedule,
callback: (triggered: Date) => void
) {
switch (typeof schedule) {
case "string":
this.schedule = parse(schedule);
break;
case "function":
this.schedule = schedule;
break;
default:
throw new Error("Invalid schedule type");
}
this.callback = callback;
this.start();
}
/** Start running the schedule */
start() {
if (this.timeout) {
return;
}
const nextTime = this.next();
if (!nextTime) {
return;
}
const diff = nextTime.getTime() - Date.now();
const delay = diff > MAX_DELAY ? MAX_DELAY : diff;
this.timeout = setTimeout(() => {
// Might still need to wait longer
if (new Date() >= nextTime) {
this.callback(nextTime);
}
// Restart
delete this.timeout;
this.start();
}, delay);
}
/**
* Get the next occurance in the schedule.
* @returns The next instant when the daemon will execute the callback or undefined if the schedule has no more possible instances. */
next(): Date | undefined {
return typeof this.schedule === "function"
? this.schedule(new Date())
: next(this.schedule, new Date());
}
/**
* Current state of the daemon.
* @returns `running` or `stopped`
*/
state(): "running" | "stopped" {
return this.timeout ? "running" : "stopped";
}
/** Stops the daemon running the schedule */
stop() {
if (this.timeout) {
clearTimeout(this.timeout);
delete this.timeout;
}
}
}