node-schedule 1.3.3 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/README.md +41 -35
- package/UPGRADING.md +20 -0
- package/index.js +16 -0
- package/lib/Invocation.js +339 -0
- package/lib/Job.js +284 -0
- package/lib/schedule.js +22 -609
- package/lib/utils/dateUtils.js +11 -0
- package/package.json +17 -9
- package/lib/nodeVersionUtils.js +0 -30
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Master (Unreleased)
|
|
2
|
+
|
|
3
|
+
# 2.0.0 - 01 February, 2021
|
|
4
|
+
|
|
5
|
+
### Breaking changes:
|
|
6
|
+
|
|
7
|
+
- Drop support for Node < 6
|
|
8
|
+
- Remove support for job objects. See `UPGRADING.md` for more details. #557
|
|
9
|
+
|
|
10
|
+
### New features:
|
|
11
|
+
|
|
12
|
+
- Introduce compatibility with browsers and Electron #553
|
|
13
|
+
|
|
14
|
+
### Bug fixes:
|
|
15
|
+
|
|
16
|
+
- Avoid leaking memory on one-off jobs #581
|
|
17
|
+
- Address anonymous job id collision problem to ensure that node-schedule could run with the unlimited uptime #582
|
|
18
|
+
|
|
19
|
+
### Internal changes:
|
|
20
|
+
|
|
21
|
+
- Bump cron-parser from 2.18.0 to 3.1.0 #562
|
|
22
|
+
- Bump sinon from 1.17.7 to 9.2.4 #566
|
|
23
|
+
- Switch to nyc from istanbul #574
|
|
24
|
+
- Convert tests from nodeunit to Tape #575
|
|
25
|
+
- Replace var with let/const #577
|
|
26
|
+
- Execute airtap tests on a headless Chromium #578
|
|
27
|
+
- Make structure more modular #579
|
package/README.md
CHANGED
|
@@ -13,31 +13,37 @@ It allows you to schedule jobs (arbitrary functions) for execution at
|
|
|
13
13
|
specific dates, with optional recurrence rules. It only uses a single timer
|
|
14
14
|
at any given time (rather than reevaluating upcoming jobs every second/minute).
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
### Installation
|
|
16
|
+
Node 6+ is supported.
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
```
|
|
23
|
-
npm install node-schedule
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
### Overview
|
|
18
|
+
## Overview
|
|
27
19
|
|
|
28
20
|
Node Schedule is for time-based scheduling, not interval-based scheduling.
|
|
21
|
+
|
|
29
22
|
While you can easily bend it to your will, if you only want to do something like
|
|
30
|
-
"run this function every 5 minutes",
|
|
31
|
-
and far more appropriate. But if you want to, say, "run this function at the :20
|
|
23
|
+
"run this function every 5 minutes", [toad-scheduler](https://github.com/kibertoad/toad-scheduler) would be a better choice. But if you want to, say, "run this function at the :20
|
|
32
24
|
and :50 of every hour on the third Tuesday of every month," you'll find that
|
|
33
25
|
Node Schedule suits your needs better. Additionally, Node Schedule has Windows
|
|
34
|
-
support unlike true cron since the node runtime is now fully supported.
|
|
26
|
+
support, unlike true `cron`, since the node runtime is now fully supported.
|
|
35
27
|
|
|
36
28
|
Note that Node Schedule is designed for in-process scheduling, i.e. scheduled jobs
|
|
37
29
|
will only fire as long as your script is running, and the schedule will disappear
|
|
38
30
|
when execution completes. If you need to schedule jobs that will persist even when
|
|
39
31
|
your script *isn't* running, consider using actual [cron].
|
|
40
32
|
|
|
33
|
+
In case you need durable jobs that persist across restarts and lock system compatible with multi-node deployments,
|
|
34
|
+
try [agenda](https://github.com/agenda/agenda) or [bree](https://github.com/breejs/bree).
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
### Installation
|
|
39
|
+
|
|
40
|
+
You can install using [npm](https://www.npmjs.com/package/node-schedule).
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
npm install node-schedule
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
|
|
41
47
|
### Jobs and Scheduling
|
|
42
48
|
|
|
43
49
|
Every scheduled job in Node Schedule is represented by a `Job` object. You can
|
|
@@ -76,9 +82,9 @@ The cron format consists of:
|
|
|
76
82
|
Examples with the cron format:
|
|
77
83
|
|
|
78
84
|
```js
|
|
79
|
-
|
|
85
|
+
const schedule = require('node-schedule');
|
|
80
86
|
|
|
81
|
-
|
|
87
|
+
const job = schedule.scheduleJob('42 * * * *', function(){
|
|
82
88
|
console.log('The answer to life, the universe, and everything!');
|
|
83
89
|
});
|
|
84
90
|
```
|
|
@@ -88,7 +94,7 @@ Execute a cron job when the minute is 42 (e.g. 19:42, 20:42, etc.).
|
|
|
88
94
|
And:
|
|
89
95
|
|
|
90
96
|
```js
|
|
91
|
-
|
|
97
|
+
const job = schedule.scheduleJob('0 17 ? * 0,4-6', function(){
|
|
92
98
|
console.log('Today is recognized by Rebecca Black!');
|
|
93
99
|
});
|
|
94
100
|
```
|
|
@@ -97,7 +103,7 @@ Execute a cron job every 5 Minutes = */5 * * * *
|
|
|
97
103
|
|
|
98
104
|
You can also get when it is scheduled to run for every invocation of the job:
|
|
99
105
|
```js
|
|
100
|
-
|
|
106
|
+
const job = schedule.scheduleJob('0 1 * * *', function(fireDate){
|
|
101
107
|
console.log('This job was supposed to run at ' + fireDate + ', but actually ran at ' + new Date());
|
|
102
108
|
});
|
|
103
109
|
```
|
|
@@ -116,10 +122,10 @@ Say you very specifically want a function to execute at 5:30am on December 21, 2
|
|
|
116
122
|
Remember - in JavaScript - 0 - January, 11 - December.
|
|
117
123
|
|
|
118
124
|
```js
|
|
119
|
-
|
|
120
|
-
|
|
125
|
+
const schedule = require('node-schedule');
|
|
126
|
+
const date = new Date(2012, 11, 21, 5, 30, 0);
|
|
121
127
|
|
|
122
|
-
|
|
128
|
+
const job = schedule.scheduleJob(date, function(){
|
|
123
129
|
console.log('The world is going to end today.');
|
|
124
130
|
});
|
|
125
131
|
```
|
|
@@ -127,10 +133,10 @@ var j = schedule.scheduleJob(date, function(){
|
|
|
127
133
|
To use current data in the future you can use binding:
|
|
128
134
|
|
|
129
135
|
```js
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
136
|
+
const schedule = require('node-schedule');
|
|
137
|
+
const date = new Date(2012, 11, 21, 5, 30, 0);
|
|
138
|
+
const x = 'Tada!';
|
|
139
|
+
const job = schedule.scheduleJob(date, function(y){
|
|
134
140
|
console.log(y);
|
|
135
141
|
}.bind(null,x));
|
|
136
142
|
x = 'Changing Data';
|
|
@@ -144,12 +150,12 @@ You can build recurrence rules to specify when a job should recur. For instance,
|
|
|
144
150
|
consider this rule, which executes the function every hour at 42 minutes after the hour:
|
|
145
151
|
|
|
146
152
|
```js
|
|
147
|
-
|
|
153
|
+
const schedule = require('node-schedule');
|
|
148
154
|
|
|
149
|
-
|
|
155
|
+
const rule = new schedule.RecurrenceRule();
|
|
150
156
|
rule.minute = 42;
|
|
151
157
|
|
|
152
|
-
|
|
158
|
+
const job = schedule.scheduleJob(rule, function(){
|
|
153
159
|
console.log('The answer to life, the universe, and everything!');
|
|
154
160
|
});
|
|
155
161
|
```
|
|
@@ -159,12 +165,12 @@ object to specify a range of start and end values, with an optional step paramet
|
|
|
159
165
|
For instance, this will print a message on Thursday, Friday, Saturday, and Sunday at 5pm:
|
|
160
166
|
|
|
161
167
|
```js
|
|
162
|
-
|
|
168
|
+
const rule = new schedule.RecurrenceRule();
|
|
163
169
|
rule.dayOfWeek = [0, new schedule.Range(4, 6)];
|
|
164
170
|
rule.hour = 17;
|
|
165
171
|
rule.minute = 0;
|
|
166
172
|
|
|
167
|
-
|
|
173
|
+
const job = schedule.scheduleJob(rule, function(){
|
|
168
174
|
console.log('Today is recognized by Rebecca Black!');
|
|
169
175
|
});
|
|
170
176
|
```
|
|
@@ -172,11 +178,11 @@ var j = schedule.scheduleJob(rule, function(){
|
|
|
172
178
|
Timezones are also supported. Here is an example of executing at the start of every day in the UTC timezone.
|
|
173
179
|
|
|
174
180
|
```js
|
|
175
|
-
|
|
181
|
+
const rule = new schedule.RecurrenceRule();
|
|
176
182
|
rule.hour = 0;
|
|
177
183
|
rule.tz = 'Etc/UTC';
|
|
178
184
|
|
|
179
|
-
|
|
185
|
+
const job = schedule.scheduleJob(rule, function(){
|
|
180
186
|
console.log('A new day has begun in the UTC timezone!');
|
|
181
187
|
});
|
|
182
188
|
```
|
|
@@ -206,7 +212,7 @@ To make things a little easier, an object literal syntax is also supported, like
|
|
|
206
212
|
in this example which will log a message every Sunday at 2:30pm:
|
|
207
213
|
|
|
208
214
|
```js
|
|
209
|
-
|
|
215
|
+
const job = schedule.scheduleJob({hour: 14, minute: 30, dayOfWeek: 0}, function(){
|
|
210
216
|
console.log('Time for tea!');
|
|
211
217
|
});
|
|
212
218
|
```
|
|
@@ -217,9 +223,9 @@ It will run after 5 seconds and stop after 10 seconds in this example.
|
|
|
217
223
|
The ruledat supports the above.
|
|
218
224
|
|
|
219
225
|
```js
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
226
|
+
const startTime = new Date(Date.now() + 5000);
|
|
227
|
+
const endTime = new Date(startTime.getTime() + 5000);
|
|
228
|
+
const job = schedule.scheduleJob({ start: startTime, end: endTime, rule: '*/1 * * * * *' }, function(){
|
|
223
229
|
console.log('Time for tea!');
|
|
224
230
|
});
|
|
225
231
|
```
|
package/UPGRADING.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
## Upgrading to new node-schedule versions
|
|
2
|
+
|
|
3
|
+
### Upgrading to version 2.0.0+
|
|
4
|
+
|
|
5
|
+
* Node.js versions older than 6 are no longer supported, please update your environment before upgrading.
|
|
6
|
+
* In order to prevent memory leaks, one-off jobs (targeted to be executed at an exact date) cannot be rescheduled by name, as reference to them is no longer stored indefinitely. If you want to keep rescheduling them, make sure to store reference to the initial job.
|
|
7
|
+
* The `scheduleJob()` method no longer supports passing an object with the job method. If you were using an object, pass the job method directly.
|
|
8
|
+
|
|
9
|
+
E.g. code that previously looked like this:
|
|
10
|
+
```javascript
|
|
11
|
+
const obj = {
|
|
12
|
+
execute() {}
|
|
13
|
+
};
|
|
14
|
+
Scheduler.scheduleJob(obj);
|
|
15
|
+
```
|
|
16
|
+
should be changed to something like this:
|
|
17
|
+
```javascript
|
|
18
|
+
function execute() {}
|
|
19
|
+
Scheduler.scheduleJob(execute);
|
|
20
|
+
```
|
package/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { cancelJob, rescheduleJob, scheduledJobs, scheduleJob} = require('./lib/schedule')
|
|
4
|
+
const { Invocation, RecurrenceRule, Range} = require('./lib/Invocation')
|
|
5
|
+
const { Job } = require('./lib/Job')
|
|
6
|
+
|
|
7
|
+
module.exports = {
|
|
8
|
+
Job,
|
|
9
|
+
Invocation,
|
|
10
|
+
Range,
|
|
11
|
+
RecurrenceRule,
|
|
12
|
+
cancelJob,
|
|
13
|
+
rescheduleJob,
|
|
14
|
+
scheduledJobs,
|
|
15
|
+
scheduleJob
|
|
16
|
+
}
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const lt = require('long-timeout')
|
|
4
|
+
const CronDate = require('cron-parser/lib/date')
|
|
5
|
+
const sorted = require('sorted-array-functions')
|
|
6
|
+
|
|
7
|
+
const invocations = [];
|
|
8
|
+
let currentInvocation = null;
|
|
9
|
+
|
|
10
|
+
/* DoesntRecur rule */
|
|
11
|
+
const DoesntRecur = new RecurrenceRule();
|
|
12
|
+
DoesntRecur.recurs = false;
|
|
13
|
+
|
|
14
|
+
/* Invocation object */
|
|
15
|
+
function Invocation(job, fireDate, recurrenceRule, endDate) {
|
|
16
|
+
this.job = job;
|
|
17
|
+
this.fireDate = fireDate;
|
|
18
|
+
this.endDate = endDate;
|
|
19
|
+
this.recurrenceRule = recurrenceRule || DoesntRecur;
|
|
20
|
+
|
|
21
|
+
this.timerID = null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function sorter(a, b) {
|
|
25
|
+
return (a.fireDate.getTime() - b.fireDate.getTime());
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* Range object */
|
|
29
|
+
function Range(start, end, step) {
|
|
30
|
+
this.start = start || 0;
|
|
31
|
+
this.end = end || 60;
|
|
32
|
+
this.step = step || 1;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
Range.prototype.contains = function(val) {
|
|
36
|
+
if (this.step === null || this.step === 1) {
|
|
37
|
+
return (val >= this.start && val <= this.end);
|
|
38
|
+
} else {
|
|
39
|
+
for (let i = this.start; i < this.end; i += this.step) {
|
|
40
|
+
if (i === val) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/* RecurrenceRule object */
|
|
50
|
+
/*
|
|
51
|
+
Interpreting each property:
|
|
52
|
+
null - any value is valid
|
|
53
|
+
number - fixed value
|
|
54
|
+
Range - value must fall in range
|
|
55
|
+
array - value must validate against any item in list
|
|
56
|
+
|
|
57
|
+
NOTE: Cron months are 1-based, but RecurrenceRule months are 0-based.
|
|
58
|
+
*/
|
|
59
|
+
function RecurrenceRule(year, month, date, dayOfWeek, hour, minute, second) {
|
|
60
|
+
this.recurs = true;
|
|
61
|
+
|
|
62
|
+
this.year = (year == null) ? null : year;
|
|
63
|
+
this.month = (month == null) ? null : month;
|
|
64
|
+
this.date = (date == null) ? null : date;
|
|
65
|
+
this.dayOfWeek = (dayOfWeek == null) ? null : dayOfWeek;
|
|
66
|
+
this.hour = (hour == null) ? null : hour;
|
|
67
|
+
this.minute = (minute == null) ? null : minute;
|
|
68
|
+
this.second = (second == null) ? 0 : second;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
RecurrenceRule.prototype.isValid = function() {
|
|
72
|
+
function isValidType(num) {
|
|
73
|
+
if (Array.isArray(num) || (num instanceof Array)) {
|
|
74
|
+
return num.every(function(e) {
|
|
75
|
+
return isValidType(e);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return !(Number.isNaN(Number(num)) && !(num instanceof Range));
|
|
79
|
+
}
|
|
80
|
+
if (this.month !== null && (this.month < 0 || this.month > 11 || !isValidType(this.month))) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
if (this.dayOfWeek !== null && (this.dayOfWeek < 0 || this.dayOfWeek > 6 || !isValidType(this.dayOfWeek))) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
if (this.hour !== null && (this.hour < 0 || this.hour > 23 || !isValidType(this.hour))) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
if (this.minute !== null && (this.minute < 0 || this.minute > 59 || !isValidType(this.minute))) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
if (this.second !== null && (this.second < 0 || this.second > 59 || !isValidType(this.second))) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
if (this.date !== null) {
|
|
96
|
+
if(!isValidType(this.date)) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
switch (this.month) {
|
|
100
|
+
case 3:
|
|
101
|
+
case 5:
|
|
102
|
+
case 8:
|
|
103
|
+
case 10:
|
|
104
|
+
if (this.date < 1 || this. date > 30) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
108
|
+
case 1:
|
|
109
|
+
if (this.date < 1 || this. date > 29) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
default:
|
|
114
|
+
if (this.date < 1 || this. date > 31) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return true;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
RecurrenceRule.prototype.nextInvocationDate = function(base) {
|
|
123
|
+
const next = this._nextInvocationDate(base);
|
|
124
|
+
return next ? next.toDate() : null;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
RecurrenceRule.prototype._nextInvocationDate = function(base) {
|
|
128
|
+
base = ((base instanceof CronDate) || (base instanceof Date)) ? base : (new Date());
|
|
129
|
+
if (!this.recurs) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if(!this.isValid()) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const now = new CronDate(Date.now(), this.tz);
|
|
138
|
+
let fullYear = now.getFullYear();
|
|
139
|
+
if ((this.year !== null) &&
|
|
140
|
+
(typeof this.year == 'number') &&
|
|
141
|
+
(this.year < fullYear)) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let next = new CronDate(base.getTime(), this.tz);
|
|
146
|
+
next.addSecond();
|
|
147
|
+
|
|
148
|
+
while (true) {
|
|
149
|
+
if (this.year !== null) {
|
|
150
|
+
fullYear = next.getFullYear();
|
|
151
|
+
if ((typeof this.year == 'number') && (this.year < fullYear)) {
|
|
152
|
+
next = null;
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!recurMatch(fullYear, this.year)) {
|
|
157
|
+
next.addYear();
|
|
158
|
+
next.setMonth(0);
|
|
159
|
+
next.setDate(1);
|
|
160
|
+
next.setHours(0);
|
|
161
|
+
next.setMinutes(0);
|
|
162
|
+
next.setSeconds(0);
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (this.month != null && !recurMatch(next.getMonth(), this.month)) {
|
|
167
|
+
next.addMonth();
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (this.date != null && !recurMatch(next.getDate(), this.date)) {
|
|
171
|
+
next.addDay();
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (this.dayOfWeek != null && !recurMatch(next.getDay(), this.dayOfWeek)) {
|
|
175
|
+
next.addDay();
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (this.hour != null && !recurMatch(next.getHours(), this.hour)) {
|
|
179
|
+
next.addHour();
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (this.minute != null && !recurMatch(next.getMinutes(), this.minute)) {
|
|
183
|
+
next.addMinute();
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (this.second != null && !recurMatch(next.getSeconds(), this.second)) {
|
|
187
|
+
next.addSecond();
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return next;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
function recurMatch(val, matcher) {
|
|
198
|
+
if (matcher == null) {
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (typeof matcher === 'number') {
|
|
203
|
+
return (val === matcher);
|
|
204
|
+
} else if(typeof matcher === 'string') {
|
|
205
|
+
return (val === Number(matcher));
|
|
206
|
+
} else if (matcher instanceof Range) {
|
|
207
|
+
return matcher.contains(val);
|
|
208
|
+
} else if (Array.isArray(matcher) || (matcher instanceof Array)) {
|
|
209
|
+
for (let i = 0; i < matcher.length; i++) {
|
|
210
|
+
if (recurMatch(val, matcher[i])) {
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* Date-based scheduler */
|
|
220
|
+
function runOnDate(date, job) {
|
|
221
|
+
const now = Date.now();
|
|
222
|
+
const then = date.getTime();
|
|
223
|
+
|
|
224
|
+
return lt.setTimeout(function() {
|
|
225
|
+
if (then > Date.now())
|
|
226
|
+
runOnDate(date, job);
|
|
227
|
+
else
|
|
228
|
+
job();
|
|
229
|
+
}, (then < now ? 0 : then - now));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function scheduleInvocation(invocation) {
|
|
233
|
+
sorted.add(invocations, invocation, sorter);
|
|
234
|
+
prepareNextInvocation();
|
|
235
|
+
const date = invocation.fireDate instanceof CronDate ? invocation.fireDate.toDate() : invocation.fireDate;
|
|
236
|
+
invocation.job.emit('scheduled', date);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function prepareNextInvocation() {
|
|
240
|
+
if (invocations.length > 0 && currentInvocation !== invocations[0]) {
|
|
241
|
+
if (currentInvocation !== null) {
|
|
242
|
+
lt.clearTimeout(currentInvocation.timerID);
|
|
243
|
+
currentInvocation.timerID = null;
|
|
244
|
+
currentInvocation = null;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
currentInvocation = invocations[0];
|
|
248
|
+
|
|
249
|
+
const job = currentInvocation.job;
|
|
250
|
+
const cinv = currentInvocation;
|
|
251
|
+
currentInvocation.timerID = runOnDate(currentInvocation.fireDate, function() {
|
|
252
|
+
currentInvocationFinished();
|
|
253
|
+
|
|
254
|
+
if (job.callback) {
|
|
255
|
+
job.callback();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (cinv.recurrenceRule.recurs || cinv.recurrenceRule._endDate === null) {
|
|
259
|
+
const inv = scheduleNextRecurrence(cinv.recurrenceRule, cinv.job, cinv.fireDate, cinv.endDate);
|
|
260
|
+
if (inv !== null) {
|
|
261
|
+
inv.job.trackInvocation(inv);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
job.stopTrackingInvocation(cinv);
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
const result = job.invoke(cinv.fireDate instanceof CronDate ? cinv.fireDate.toDate() : cinv.fireDate);
|
|
269
|
+
job.emit('run');
|
|
270
|
+
|
|
271
|
+
if (result instanceof Promise) {
|
|
272
|
+
result.catch(function (err) {
|
|
273
|
+
job.emit('error', err);
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
} catch (err) {
|
|
277
|
+
job.emit('error', err);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (job.isOneTimeJob) {
|
|
281
|
+
job.deleteFromSchedule();
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function currentInvocationFinished() {
|
|
288
|
+
invocations.shift();
|
|
289
|
+
currentInvocation = null;
|
|
290
|
+
prepareNextInvocation();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function cancelInvocation(invocation) {
|
|
294
|
+
const idx = invocations.indexOf(invocation);
|
|
295
|
+
if (idx > -1) {
|
|
296
|
+
invocations.splice(idx, 1);
|
|
297
|
+
if (invocation.timerID !== null) {
|
|
298
|
+
lt.clearTimeout(invocation.timerID);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (currentInvocation === invocation) {
|
|
302
|
+
currentInvocation = null;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
invocation.job.emit('canceled', invocation.fireDate);
|
|
306
|
+
prepareNextInvocation();
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/* Recurrence scheduler */
|
|
311
|
+
function scheduleNextRecurrence(rule, job, prevDate, endDate) {
|
|
312
|
+
|
|
313
|
+
prevDate = (prevDate instanceof CronDate) ? prevDate : new CronDate();
|
|
314
|
+
|
|
315
|
+
const date = (rule instanceof RecurrenceRule) ? rule._nextInvocationDate(prevDate) : rule.next();
|
|
316
|
+
if (date === null) {
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if ((endDate instanceof CronDate) && date.getTime() > endDate.getTime()) {
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const inv = new Invocation(job, date, rule, endDate);
|
|
325
|
+
scheduleInvocation(inv);
|
|
326
|
+
|
|
327
|
+
return inv;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
module.exports = {
|
|
331
|
+
Range,
|
|
332
|
+
RecurrenceRule,
|
|
333
|
+
Invocation,
|
|
334
|
+
cancelInvocation,
|
|
335
|
+
scheduleInvocation,
|
|
336
|
+
scheduleNextRecurrence,
|
|
337
|
+
sorter,
|
|
338
|
+
_invocations: invocations
|
|
339
|
+
}
|