@radically-straightforward/utilities 1.2.2 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +8 -0
- package/README.md +44 -71
- package/build/index.d.mts +34 -57
- package/build/index.d.mts.map +1 -1
- package/build/index.mjs +66 -89
- package/build/index.mjs.map +1 -1
- package/build/index.test.mjs +20 -39
- package/build/index.test.mjs.map +1 -1
- package/package.json +6 -9
- package/source/index.mts +86 -101
- package/source/index.test.mts +29 -57
- package/tsconfig.json +1 -1
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 2.0.1 · 2024-04-26
|
4
|
+
|
5
|
+
- `backgroundJob()` doesn’t stop when the job throws an exception.
|
6
|
+
|
7
|
+
## 2.0.0 · 2024-04-09
|
8
|
+
|
9
|
+
- **Breaking Change:** Made `@radically-straightforward/utilities` truly independent of platform by extracting the Node.js especial treatment into `@radically-straightforward/node`.
|
10
|
+
|
3
11
|
## 1.2.2 · 2024-04-01
|
4
12
|
|
5
13
|
- Added:
|
package/README.md
CHANGED
@@ -16,69 +16,6 @@ import * as utilities from "@radically-straightforward/utilities";
|
|
16
16
|
|
17
17
|
<!-- DOCUMENTATION START: ./source/index.mts -->
|
18
18
|
|
19
|
-
### `backgroundJob()`
|
20
|
-
|
21
|
-
```typescript
|
22
|
-
export function backgroundJob(
|
23
|
-
{
|
24
|
-
interval,
|
25
|
-
intervalVariance = 0.1,
|
26
|
-
}: {
|
27
|
-
interval: number;
|
28
|
-
intervalVariance?: number;
|
29
|
-
},
|
30
|
-
job: () => void | Promise<void>,
|
31
|
-
): {
|
32
|
-
run: () => void;
|
33
|
-
stop: () => void;
|
34
|
-
};
|
35
|
-
```
|
36
|
-
|
37
|
-
Start a background job that runs every `interval`.
|
38
|
-
|
39
|
-
This is different from `setInterval()` in the following ways:
|
40
|
-
|
41
|
-
1. The interval counts **between** jobs, so slow background jobs don’t get called concurrently:
|
42
|
-
|
43
|
-
```
|
44
|
-
setInterval()
|
45
|
-
| SLOW BACKGROUND JOB |
|
46
|
-
| INTERVAL | SLOW BACKGROUND JOB |
|
47
|
-
| INTERVAL | ...
|
48
|
-
|
49
|
-
backgroundJob()
|
50
|
-
| SLOW BACKGROUND JOB | INTERVAL | SLOW BACKGROUND JOB | INTERVAL | ...
|
51
|
-
```
|
52
|
-
|
53
|
-
2. We introduce a random `intervalVariance` to avoid many background jobs from starting at the same time and overloading the machine.
|
54
|
-
|
55
|
-
3. You may use `backgroundJob.run()` to force the background job to run right away. If the background job is already running, calling `backgroundJob.run()` schedules it to run again as soon as possible (with a wait interval of 0).
|
56
|
-
|
57
|
-
4. You may use `backgroundJob.stop()` to stop the background job. If the background job is running, it will finish but it will not be scheduled to run again. This is similar to how an HTTP server may terminate gracefully by stopping accepting new requests but finishing responding to existing requests. After a job has been stopped, you may not `backgroundJob.run()` it again (calling `backgroundJob.run()` has no effect).
|
58
|
-
|
59
|
-
5. In Node.js the background job is stopped on [`"gracefulTermination"`](https://github.com/radically-straightforward/radically-straightforward/tree/main/node#graceful-termination).
|
60
|
-
|
61
|
-
**Example**
|
62
|
-
|
63
|
-
```javascript
|
64
|
-
import * as utilities from "@radically-straightforward/utilities";
|
65
|
-
|
66
|
-
const backgroundJob = utilities.backgroundJob(
|
67
|
-
{ interval: 3 * 1000 },
|
68
|
-
async () => {
|
69
|
-
console.log("backgroundJob(): Running background job...");
|
70
|
-
await utilities.sleep(3 * 1000);
|
71
|
-
console.log("backgroundJob(): ...finished running background job.");
|
72
|
-
},
|
73
|
-
);
|
74
|
-
process.on("SIGTSTP", () => {
|
75
|
-
backgroundJob.run();
|
76
|
-
});
|
77
|
-
console.log(
|
78
|
-
"backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...",
|
79
|
-
);
|
80
|
-
```
|
81
|
-
|
82
19
|
### `sleep()`
|
83
20
|
|
84
21
|
```typescript
|
@@ -158,14 +95,6 @@ export const ISODateRegExp: RegExp;
|
|
158
95
|
|
159
96
|
A regular expression that matches ISO dates, for example, `2024-04-01T14:19:48.162Z`.
|
160
97
|
|
161
|
-
### `localizedDateRegExp`
|
162
|
-
|
163
|
-
```typescript
|
164
|
-
export const localizedDateRegExp: RegExp;
|
165
|
-
```
|
166
|
-
|
167
|
-
A regular expression that matches localized dates, for example, `2024-04-01 15:20`.
|
168
|
-
|
169
98
|
### `Intern`
|
170
99
|
|
171
100
|
```typescript
|
@@ -307,4 +236,48 @@ Similar to `collections-deep-equal` but either incomplete, or lacking type defin
|
|
307
236
|
- <https://twitter.com/swannodette/status/1067962983924539392>
|
308
237
|
- <https://gist.github.com/modernserf/c000e62d40f678cf395e3f360b9b0e43>
|
309
238
|
|
239
|
+
### `backgroundJob()`
|
240
|
+
|
241
|
+
```typescript
|
242
|
+
export function backgroundJob(
|
243
|
+
{
|
244
|
+
interval,
|
245
|
+
onStop = () => {},
|
246
|
+
}: {
|
247
|
+
interval: number;
|
248
|
+
onStop?: () => void | Promise<void>;
|
249
|
+
},
|
250
|
+
job: () => void | Promise<void>,
|
251
|
+
): {
|
252
|
+
run: () => Promise<void>;
|
253
|
+
stop: () => Promise<void>;
|
254
|
+
};
|
255
|
+
```
|
256
|
+
|
257
|
+
> **Note:** This is a lower level utility. See `@radically-straightforward/node`’s and `@radically-straightforward/javascript`’s extensions to `backgroundJob()` that are better suited for their specific environments.
|
258
|
+
|
259
|
+
Start a background job that runs every `interval`.
|
260
|
+
|
261
|
+
`backgroundJob()` is different from `setInterval()` in the following ways:
|
262
|
+
|
263
|
+
1. The interval counts **between** jobs, so slow background jobs don’t get called concurrently:
|
264
|
+
|
265
|
+
```
|
266
|
+
setInterval()
|
267
|
+
| SLOW BACKGROUND JOB |
|
268
|
+
| INTERVAL | SLOW BACKGROUND JOB |
|
269
|
+
| INTERVAL | ...
|
270
|
+
|
271
|
+
backgroundJob()
|
272
|
+
| SLOW BACKGROUND JOB | INTERVAL | SLOW BACKGROUND JOB | INTERVAL | ...
|
273
|
+
```
|
274
|
+
|
275
|
+
2. You may use `backgroundJob.run()` to force the background job to run right away. If the background job is already running, calling `backgroundJob.run()` schedules it to run again as soon as possible (with a wait interval of `0`).
|
276
|
+
|
277
|
+
3. You may use `backgroundJob.stop()` to stop the background job. If the background job is in the middle of running, it will finish but it will not be scheduled to run again. This is similar to how an HTTP server may terminate gracefully by stopping accepting new requests but finishing responding to existing requests. After a job has been stopped, you may not `backgroundJob.run()` it again (calling `backgroundJob.run()` has no effect).
|
278
|
+
|
279
|
+
4. We introduce a random interval variance of 10% on top of the given `interval` to avoid many background jobs from starting at the same time and overloading the machine.
|
280
|
+
|
281
|
+
> **Note:** If the job throws an exception, the exception is logged and the background job continues.
|
282
|
+
|
310
283
|
<!-- DOCUMENTATION END: ./source/index.mts -->
|
package/build/index.d.mts
CHANGED
@@ -1,56 +1,3 @@
|
|
1
|
-
/**
|
2
|
-
* Start a background job that runs every `interval`.
|
3
|
-
*
|
4
|
-
* This is different from `setInterval()` in the following ways:
|
5
|
-
*
|
6
|
-
* 1. The interval counts **between** jobs, so slow background jobs don’t get called concurrently:
|
7
|
-
*
|
8
|
-
* ```
|
9
|
-
* setInterval()
|
10
|
-
* | SLOW BACKGROUND JOB |
|
11
|
-
* | INTERVAL | SLOW BACKGROUND JOB |
|
12
|
-
* | INTERVAL | ...
|
13
|
-
*
|
14
|
-
* backgroundJob()
|
15
|
-
* | SLOW BACKGROUND JOB | INTERVAL | SLOW BACKGROUND JOB | INTERVAL | ...
|
16
|
-
* ```
|
17
|
-
*
|
18
|
-
* 2. We introduce a random `intervalVariance` to avoid many background jobs from starting at the same time and overloading the machine.
|
19
|
-
*
|
20
|
-
* 3. You may use `backgroundJob.run()` to force the background job to run right away. If the background job is already running, calling `backgroundJob.run()` schedules it to run again as soon as possible (with a wait interval of 0).
|
21
|
-
*
|
22
|
-
* 4. You may use `backgroundJob.stop()` to stop the background job. If the background job is running, it will finish but it will not be scheduled to run again. This is similar to how an HTTP server may terminate gracefully by stopping accepting new requests but finishing responding to existing requests. After a job has been stopped, you may not `backgroundJob.run()` it again (calling `backgroundJob.run()` has no effect).
|
23
|
-
*
|
24
|
-
* 5. In Node.js the background job is stopped on [`"gracefulTermination"`](https://github.com/radically-straightforward/radically-straightforward/tree/main/node#graceful-termination).
|
25
|
-
*
|
26
|
-
* **Example**
|
27
|
-
*
|
28
|
-
* ```javascript
|
29
|
-
* import * as utilities from "@radically-straightforward/utilities";
|
30
|
-
*
|
31
|
-
* const backgroundJob = utilities.backgroundJob(
|
32
|
-
* { interval: 3 * 1000 },
|
33
|
-
* async () => {
|
34
|
-
* console.log("backgroundJob(): Running background job...");
|
35
|
-
* await utilities.sleep(3 * 1000);
|
36
|
-
* console.log("backgroundJob(): ...finished running background job.");
|
37
|
-
* },
|
38
|
-
* );
|
39
|
-
* process.on("SIGTSTP", () => {
|
40
|
-
* backgroundJob.run();
|
41
|
-
* });
|
42
|
-
* console.log(
|
43
|
-
* "backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...",
|
44
|
-
* );
|
45
|
-
* ```
|
46
|
-
*/
|
47
|
-
export declare function backgroundJob({ interval, intervalVariance, }: {
|
48
|
-
interval: number;
|
49
|
-
intervalVariance?: number;
|
50
|
-
}, job: () => void | Promise<void>): {
|
51
|
-
run: () => void;
|
52
|
-
stop: () => void;
|
53
|
-
};
|
54
1
|
/**
|
55
2
|
* A promisified version of `setTimeout()`. Bare-bones: It doesn’t even offer a way to `clearTimeout()`. Useful in JavaScript that may run in the browser—if you’re only targeting Node.js then you’re better served by [`timersPromises.setTimeout()`](https://nodejs.org/api/timers.html#timerspromisessettimeoutdelay-value-options).
|
56
3
|
*/
|
@@ -100,10 +47,6 @@ export declare const emailRegExp: RegExp;
|
|
100
47
|
* A regular expression that matches ISO dates, for example, `2024-04-01T14:19:48.162Z`.
|
101
48
|
*/
|
102
49
|
export declare const ISODateRegExp: RegExp;
|
103
|
-
/**
|
104
|
-
* A regular expression that matches localized dates, for example, `2024-04-01 15:20`.
|
105
|
-
*/
|
106
|
-
export declare const localizedDateRegExp: RegExp;
|
107
50
|
/**
|
108
51
|
* Utility type for `intern()`.
|
109
52
|
*/
|
@@ -236,4 +179,38 @@ export declare namespace intern {
|
|
236
179
|
}>;
|
237
180
|
}
|
238
181
|
export declare const internSymbol: unique symbol;
|
182
|
+
/**
|
183
|
+
* > **Note:** This is a lower level utility. See `@radically-straightforward/node`’s and `@radically-straightforward/javascript`’s extensions to `backgroundJob()` that are better suited for their specific environments.
|
184
|
+
*
|
185
|
+
* Start a background job that runs every `interval`.
|
186
|
+
*
|
187
|
+
* `backgroundJob()` is different from `setInterval()` in the following ways:
|
188
|
+
*
|
189
|
+
* 1. The interval counts **between** jobs, so slow background jobs don’t get called concurrently:
|
190
|
+
*
|
191
|
+
* ```
|
192
|
+
* setInterval()
|
193
|
+
* | SLOW BACKGROUND JOB |
|
194
|
+
* | INTERVAL | SLOW BACKGROUND JOB |
|
195
|
+
* | INTERVAL | ...
|
196
|
+
*
|
197
|
+
* backgroundJob()
|
198
|
+
* | SLOW BACKGROUND JOB | INTERVAL | SLOW BACKGROUND JOB | INTERVAL | ...
|
199
|
+
* ```
|
200
|
+
*
|
201
|
+
* 2. You may use `backgroundJob.run()` to force the background job to run right away. If the background job is already running, calling `backgroundJob.run()` schedules it to run again as soon as possible (with a wait interval of `0`).
|
202
|
+
*
|
203
|
+
* 3. You may use `backgroundJob.stop()` to stop the background job. If the background job is in the middle of running, it will finish but it will not be scheduled to run again. This is similar to how an HTTP server may terminate gracefully by stopping accepting new requests but finishing responding to existing requests. After a job has been stopped, you may not `backgroundJob.run()` it again (calling `backgroundJob.run()` has no effect).
|
204
|
+
*
|
205
|
+
* 4. We introduce a random interval variance of 10% on top of the given `interval` to avoid many background jobs from starting at the same time and overloading the machine.
|
206
|
+
*
|
207
|
+
* > **Note:** If the job throws an exception, the exception is logged and the background job continues.
|
208
|
+
*/
|
209
|
+
export declare function backgroundJob({ interval, onStop, }: {
|
210
|
+
interval: number;
|
211
|
+
onStop?: () => void | Promise<void>;
|
212
|
+
}, job: () => void | Promise<void>): {
|
213
|
+
run: () => Promise<void>;
|
214
|
+
stop: () => Promise<void>;
|
215
|
+
};
|
239
216
|
//# sourceMappingURL=index.d.mts.map
|
package/build/index.d.mts.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../source/index.mts"],"names":[],"mappings":"
|
1
|
+
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../source/index.mts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAErD;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;GAEG;AACH,wBAAgB,GAAG,CAAC,GAAG,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI,CAEnD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,wBAAyB,SAAQ,eAAe;;CAkB5D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAIjD;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAI9C;AAED;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,MAAmD,CAAC;AAE9E;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,MACqB,CAAC;AAElD;;GAEG;AACH,MAAM,MAAM,MAAM,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,GAAG;IAAE,CAAC,YAAY,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC,CAAC;AAErE;;GAEG;AACH,MAAM,MAAM,gBAAgB,GACxB,MAAM,GACN,MAAM,GACN,MAAM,GACN,OAAO,GACP,MAAM,GACN,SAAS,GACT,IAAI,GACJ,MAAM,CAAC,OAAO,CAAC,CAAC;AAEpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqGG;AACH,wBAAgB,MAAM,CACpB,IAAI,SAAS,KAAK,CAAC,gBAAgB,CAAC,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAAA;CAAE,EAC1E,KAAK,EAAE,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAyC3B;yBA3Ce,MAAM;;;;;;;;;;;;;;;;AA6CtB,eAAO,MAAM,YAAY,eAAmB,CAAC;AAiB7C;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,aAAa,CAC3B,EACE,QAAQ,EACR,MAAiB,GAClB,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC,EACD,GAAG,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAC9B;IACD,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B,CA6CA"}
|
package/build/index.mjs
CHANGED
@@ -1,88 +1,3 @@
|
|
1
|
-
if (process !== undefined)
|
2
|
-
await import("@radically-straightforward/node");
|
3
|
-
/**
|
4
|
-
* Start a background job that runs every `interval`.
|
5
|
-
*
|
6
|
-
* This is different from `setInterval()` in the following ways:
|
7
|
-
*
|
8
|
-
* 1. The interval counts **between** jobs, so slow background jobs don’t get called concurrently:
|
9
|
-
*
|
10
|
-
* ```
|
11
|
-
* setInterval()
|
12
|
-
* | SLOW BACKGROUND JOB |
|
13
|
-
* | INTERVAL | SLOW BACKGROUND JOB |
|
14
|
-
* | INTERVAL | ...
|
15
|
-
*
|
16
|
-
* backgroundJob()
|
17
|
-
* | SLOW BACKGROUND JOB | INTERVAL | SLOW BACKGROUND JOB | INTERVAL | ...
|
18
|
-
* ```
|
19
|
-
*
|
20
|
-
* 2. We introduce a random `intervalVariance` to avoid many background jobs from starting at the same time and overloading the machine.
|
21
|
-
*
|
22
|
-
* 3. You may use `backgroundJob.run()` to force the background job to run right away. If the background job is already running, calling `backgroundJob.run()` schedules it to run again as soon as possible (with a wait interval of 0).
|
23
|
-
*
|
24
|
-
* 4. You may use `backgroundJob.stop()` to stop the background job. If the background job is running, it will finish but it will not be scheduled to run again. This is similar to how an HTTP server may terminate gracefully by stopping accepting new requests but finishing responding to existing requests. After a job has been stopped, you may not `backgroundJob.run()` it again (calling `backgroundJob.run()` has no effect).
|
25
|
-
*
|
26
|
-
* 5. In Node.js the background job is stopped on [`"gracefulTermination"`](https://github.com/radically-straightforward/radically-straightforward/tree/main/node#graceful-termination).
|
27
|
-
*
|
28
|
-
* **Example**
|
29
|
-
*
|
30
|
-
* ```javascript
|
31
|
-
* import * as utilities from "@radically-straightforward/utilities";
|
32
|
-
*
|
33
|
-
* const backgroundJob = utilities.backgroundJob(
|
34
|
-
* { interval: 3 * 1000 },
|
35
|
-
* async () => {
|
36
|
-
* console.log("backgroundJob(): Running background job...");
|
37
|
-
* await utilities.sleep(3 * 1000);
|
38
|
-
* console.log("backgroundJob(): ...finished running background job.");
|
39
|
-
* },
|
40
|
-
* );
|
41
|
-
* process.on("SIGTSTP", () => {
|
42
|
-
* backgroundJob.run();
|
43
|
-
* });
|
44
|
-
* console.log(
|
45
|
-
* "backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...",
|
46
|
-
* );
|
47
|
-
* ```
|
48
|
-
*/
|
49
|
-
export function backgroundJob({ interval, intervalVariance = 0.1, }, job) {
|
50
|
-
let state = "sleeping";
|
51
|
-
let timeout = undefined;
|
52
|
-
const scheduler = {
|
53
|
-
run: async () => {
|
54
|
-
switch (state) {
|
55
|
-
case "sleeping":
|
56
|
-
clearTimeout(timeout);
|
57
|
-
state = "running";
|
58
|
-
await job();
|
59
|
-
if (state === "running" || state === "runningAndMarkedForRerun") {
|
60
|
-
timeout = setTimeout(() => {
|
61
|
-
scheduler.run();
|
62
|
-
}, state === "runningAndMarkedForRerun"
|
63
|
-
? 0
|
64
|
-
: interval + interval * intervalVariance * Math.random());
|
65
|
-
state = "sleeping";
|
66
|
-
}
|
67
|
-
break;
|
68
|
-
case "running":
|
69
|
-
state = "runningAndMarkedForRerun";
|
70
|
-
break;
|
71
|
-
}
|
72
|
-
},
|
73
|
-
stop: () => {
|
74
|
-
clearTimeout(timeout);
|
75
|
-
state = "stopped";
|
76
|
-
process?.off?.("gracefulTermination", gracefulTerminationEventListener);
|
77
|
-
},
|
78
|
-
};
|
79
|
-
scheduler.run();
|
80
|
-
const gracefulTerminationEventListener = () => {
|
81
|
-
scheduler.stop();
|
82
|
-
};
|
83
|
-
process?.once?.("gracefulTermination", gracefulTerminationEventListener);
|
84
|
-
return scheduler;
|
85
|
-
}
|
86
1
|
/**
|
87
2
|
* A promisified version of `setTimeout()`. Bare-bones: It doesn’t even offer a way to `clearTimeout()`. Useful in JavaScript that may run in the browser—if you’re only targeting Node.js then you’re better served by [`timersPromises.setTimeout()`](https://nodejs.org/api/timers.html#timerspromisessettimeoutdelay-value-options).
|
88
3
|
*/
|
@@ -161,10 +76,6 @@ export const emailRegExp = /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/i;
|
|
161
76
|
* A regular expression that matches ISO dates, for example, `2024-04-01T14:19:48.162Z`.
|
162
77
|
*/
|
163
78
|
export const ISODateRegExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
|
164
|
-
/**
|
165
|
-
* A regular expression that matches localized dates, for example, `2024-04-01 15:20`.
|
166
|
-
*/
|
167
|
-
export const localizedDateRegExp = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/;
|
168
79
|
/**
|
169
80
|
* [Interning](<https://en.wikipedia.org/wiki/Interning_(computer_science)>) a value makes it unique across the program, which is useful for checking equality with `===` (reference equality), using it as a key in a `Map`, adding it to a `Set`, and so forth:
|
170
81
|
*
|
@@ -309,4 +220,70 @@ intern.pool = {
|
|
309
220
|
intern.finalizationRegistry = new FinalizationRegistry(({ type, key }) => {
|
310
221
|
intern.pool[type].delete(key);
|
311
222
|
});
|
223
|
+
/**
|
224
|
+
* > **Note:** This is a lower level utility. See `@radically-straightforward/node`’s and `@radically-straightforward/javascript`’s extensions to `backgroundJob()` that are better suited for their specific environments.
|
225
|
+
*
|
226
|
+
* Start a background job that runs every `interval`.
|
227
|
+
*
|
228
|
+
* `backgroundJob()` is different from `setInterval()` in the following ways:
|
229
|
+
*
|
230
|
+
* 1. The interval counts **between** jobs, so slow background jobs don’t get called concurrently:
|
231
|
+
*
|
232
|
+
* ```
|
233
|
+
* setInterval()
|
234
|
+
* | SLOW BACKGROUND JOB |
|
235
|
+
* | INTERVAL | SLOW BACKGROUND JOB |
|
236
|
+
* | INTERVAL | ...
|
237
|
+
*
|
238
|
+
* backgroundJob()
|
239
|
+
* | SLOW BACKGROUND JOB | INTERVAL | SLOW BACKGROUND JOB | INTERVAL | ...
|
240
|
+
* ```
|
241
|
+
*
|
242
|
+
* 2. You may use `backgroundJob.run()` to force the background job to run right away. If the background job is already running, calling `backgroundJob.run()` schedules it to run again as soon as possible (with a wait interval of `0`).
|
243
|
+
*
|
244
|
+
* 3. You may use `backgroundJob.stop()` to stop the background job. If the background job is in the middle of running, it will finish but it will not be scheduled to run again. This is similar to how an HTTP server may terminate gracefully by stopping accepting new requests but finishing responding to existing requests. After a job has been stopped, you may not `backgroundJob.run()` it again (calling `backgroundJob.run()` has no effect).
|
245
|
+
*
|
246
|
+
* 4. We introduce a random interval variance of 10% on top of the given `interval` to avoid many background jobs from starting at the same time and overloading the machine.
|
247
|
+
*
|
248
|
+
* > **Note:** If the job throws an exception, the exception is logged and the background job continues.
|
249
|
+
*/
|
250
|
+
export function backgroundJob({ interval, onStop = () => { }, }, job) {
|
251
|
+
let state = "sleeping";
|
252
|
+
let timeout = setTimeout(() => { });
|
253
|
+
const backgroundJob = {
|
254
|
+
run: async () => {
|
255
|
+
switch (state) {
|
256
|
+
case "sleeping":
|
257
|
+
clearTimeout(timeout);
|
258
|
+
state = "running";
|
259
|
+
try {
|
260
|
+
await job();
|
261
|
+
}
|
262
|
+
catch (error) {
|
263
|
+
log("BACKGROUND JOB ERROR", String(error), error?.stack ?? "");
|
264
|
+
}
|
265
|
+
if (state === "running" || state === "runningAndMarkedForRerun") {
|
266
|
+
timeout = setTimeout(() => {
|
267
|
+
backgroundJob.run();
|
268
|
+
}, state ===
|
269
|
+
"runningAndMarkedForRerun"
|
270
|
+
? 0
|
271
|
+
: interval * (1 + 0.1 * Math.random()));
|
272
|
+
state = "sleeping";
|
273
|
+
}
|
274
|
+
break;
|
275
|
+
case "running":
|
276
|
+
state = "runningAndMarkedForRerun";
|
277
|
+
break;
|
278
|
+
}
|
279
|
+
},
|
280
|
+
stop: async () => {
|
281
|
+
clearTimeout(timeout);
|
282
|
+
state = "stopped";
|
283
|
+
await onStop();
|
284
|
+
},
|
285
|
+
};
|
286
|
+
backgroundJob.run();
|
287
|
+
return backgroundJob;
|
288
|
+
}
|
312
289
|
//# sourceMappingURL=index.mjs.map
|
package/build/index.mjs.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../source/index.mts"],"names":[],"mappings":"AAAA
|
1
|
+
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../source/index.mts"],"names":[],"mappings":"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"}
|
package/build/index.test.mjs
CHANGED
@@ -2,26 +2,6 @@ import test from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
3
3
|
import * as utilities from "@radically-straightforward/utilities";
|
4
4
|
import { intern as $ } from "@radically-straightforward/utilities";
|
5
|
-
test("backgroundJob()", {
|
6
|
-
skip: process.stdin.isTTY
|
7
|
-
? false
|
8
|
-
: "Run interactive test with ‘node ./build/index.test.mjs’.",
|
9
|
-
}, async () => {
|
10
|
-
for (let iteration = 0; iteration < 1000; iteration++) {
|
11
|
-
const backgroundJob = utilities.backgroundJob({ interval: 3 * 1000 }, () => { });
|
12
|
-
backgroundJob.stop();
|
13
|
-
// If background jobs leak ‘process.once("gracefulTermination")’ event listeners, then we get a warning in the console.
|
14
|
-
}
|
15
|
-
const backgroundJob = utilities.backgroundJob({ interval: 3 * 1000 }, async () => {
|
16
|
-
console.log("backgroundJob(): Running background job...");
|
17
|
-
await utilities.sleep(3 * 1000);
|
18
|
-
console.log("backgroundJob(): ...finished running background job.");
|
19
|
-
});
|
20
|
-
process.on("SIGTSTP", () => {
|
21
|
-
backgroundJob.run();
|
22
|
-
});
|
23
|
-
console.log("backgroundJob(): Press ⌃Z to force background job to run and ⌃C to gracefully terminate...");
|
24
|
-
});
|
25
5
|
test("sleep()", async () => {
|
26
6
|
const before = Date.now();
|
27
7
|
await utilities.sleep(1000);
|
@@ -78,10 +58,6 @@ test("ISODateRegExp", () => {
|
|
78
58
|
assert.match("2024-04-01T14:19:48.162Z", utilities.ISODateRegExp);
|
79
59
|
assert.doesNotMatch("2024-04-01 15:20", utilities.ISODateRegExp);
|
80
60
|
});
|
81
|
-
test("localizedDateRegExp", () => {
|
82
|
-
assert.match("2024-04-01 15:20", utilities.localizedDateRegExp);
|
83
|
-
assert.doesNotMatch("2024-04-01T14:19:48.162Z", utilities.localizedDateRegExp);
|
84
|
-
});
|
85
61
|
test("intern()", () => {
|
86
62
|
// @ts-expect-error
|
87
63
|
assert(([1] === [1]) === false);
|
@@ -125,20 +101,25 @@ test("intern()", () => {
|
|
125
101
|
// @ts-expect-error
|
126
102
|
$([1])[0] = 2;
|
127
103
|
});
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
104
|
+
});
|
105
|
+
test("backgroundJob()", {
|
106
|
+
skip: process.stdin.isTTY
|
107
|
+
? false
|
108
|
+
: "Run interactive test with ‘node ./build/index.test.mjs’.",
|
109
|
+
}, async () => {
|
110
|
+
const backgroundJob = utilities.backgroundJob({ interval: 3 * 1000 }, async () => {
|
111
|
+
console.log("backgroundJob(): Running background job...");
|
112
|
+
await utilities.sleep(3 * 1000);
|
113
|
+
console.log("backgroundJob(): ...finished running background job.");
|
114
|
+
if (Math.random() < 0.3)
|
115
|
+
throw new Error("There’s a 30% chance that the background job results in error.");
|
116
|
+
});
|
117
|
+
process.on("SIGTSTP", () => {
|
118
|
+
backgroundJob.run();
|
119
|
+
});
|
120
|
+
process.on("SIGINT", () => {
|
121
|
+
backgroundJob.stop();
|
122
|
+
});
|
123
|
+
console.log("backgroundJob(): Press ⌃Z to ‘run()’ and ⌃C to ‘stop()’...");
|
143
124
|
});
|
144
125
|
//# sourceMappingURL=index.test.mjs.map
|
package/build/index.test.mjs.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.test.mjs","sourceRoot":"","sources":["../source/index.test.mts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,KAAK,SAAS,MAAM,sCAAsC,CAAC;AAClE,OAAO,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,sCAAsC,CAAC;AAEnE,IAAI,
|
1
|
+
{"version":3,"file":"index.test.mjs","sourceRoot":"","sources":["../source/index.test.mts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@radically-straightforward/utilities",
|
3
|
-
"version": "
|
3
|
+
"version": "2.0.1",
|
4
4
|
"description": "🛠️ Utilities for Node.js and the browser",
|
5
5
|
"keywords": [
|
6
6
|
"node",
|
@@ -27,15 +27,12 @@
|
|
27
27
|
"prepare": "tsc && documentation",
|
28
28
|
"test": "npm run prepare && node --test && prettier --check \"./README.md\" --check \"./CHANGELOG.md\" --check \"./package.json\" --check \"./tsconfig.json\" --check \"./source/**/*.mts\""
|
29
29
|
},
|
30
|
-
"dependencies": {
|
31
|
-
"@radically-straightforward/node": "^3.0.0"
|
32
|
-
},
|
33
30
|
"devDependencies": {
|
34
|
-
"@radically-straightforward/documentation": "^1.0.
|
35
|
-
"@radically-straightforward/
|
36
|
-
"@types/node": "^20.
|
37
|
-
"prettier": "^3.
|
38
|
-
"typescript": "^5.
|
31
|
+
"@radically-straightforward/documentation": "^1.0.4",
|
32
|
+
"@radically-straightforward/typescript": "^1.0.0",
|
33
|
+
"@types/node": "^20.12.6",
|
34
|
+
"prettier": "^3.2.5",
|
35
|
+
"typescript": "^5.4.4"
|
39
36
|
},
|
40
37
|
"prettier": {}
|
41
38
|
}
|
package/source/index.mts
CHANGED
@@ -1,99 +1,3 @@
|
|
1
|
-
if (process !== undefined) await import("@radically-straightforward/node");
|
2
|
-
|
3
|
-
/**
|
4
|
-
* Start a background job that runs every `interval`.
|
5
|
-
*
|
6
|
-
* This is different from `setInterval()` in the following ways:
|
7
|
-
*
|
8
|
-
* 1. The interval counts **between** jobs, so slow background jobs don’t get called concurrently:
|
9
|
-
*
|
10
|
-
* ```
|
11
|
-
* setInterval()
|
12
|
-
* | SLOW BACKGROUND JOB |
|
13
|
-
* | INTERVAL | SLOW BACKGROUND JOB |
|
14
|
-
* | INTERVAL | ...
|
15
|
-
*
|
16
|
-
* backgroundJob()
|
17
|
-
* | SLOW BACKGROUND JOB | INTERVAL | SLOW BACKGROUND JOB | INTERVAL | ...
|
18
|
-
* ```
|
19
|
-
*
|
20
|
-
* 2. We introduce a random `intervalVariance` to avoid many background jobs from starting at the same time and overloading the machine.
|
21
|
-
*
|
22
|
-
* 3. You may use `backgroundJob.run()` to force the background job to run right away. If the background job is already running, calling `backgroundJob.run()` schedules it to run again as soon as possible (with a wait interval of 0).
|
23
|
-
*
|
24
|
-
* 4. You may use `backgroundJob.stop()` to stop the background job. If the background job is running, it will finish but it will not be scheduled to run again. This is similar to how an HTTP server may terminate gracefully by stopping accepting new requests but finishing responding to existing requests. After a job has been stopped, you may not `backgroundJob.run()` it again (calling `backgroundJob.run()` has no effect).
|
25
|
-
*
|
26
|
-
* 5. In Node.js the background job is stopped on [`"gracefulTermination"`](https://github.com/radically-straightforward/radically-straightforward/tree/main/node#graceful-termination).
|
27
|
-
*
|
28
|
-
* **Example**
|
29
|
-
*
|
30
|
-
* ```javascript
|
31
|
-
* import * as utilities from "@radically-straightforward/utilities";
|
32
|
-
*
|
33
|
-
* const backgroundJob = utilities.backgroundJob(
|
34
|
-
* { interval: 3 * 1000 },
|
35
|
-
* async () => {
|
36
|
-
* console.log("backgroundJob(): Running background job...");
|
37
|
-
* await utilities.sleep(3 * 1000);
|
38
|
-
* console.log("backgroundJob(): ...finished running background job.");
|
39
|
-
* },
|
40
|
-
* );
|
41
|
-
* process.on("SIGTSTP", () => {
|
42
|
-
* backgroundJob.run();
|
43
|
-
* });
|
44
|
-
* console.log(
|
45
|
-
* "backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...",
|
46
|
-
* );
|
47
|
-
* ```
|
48
|
-
*/
|
49
|
-
export function backgroundJob(
|
50
|
-
{
|
51
|
-
interval,
|
52
|
-
intervalVariance = 0.1,
|
53
|
-
}: { interval: number; intervalVariance?: number },
|
54
|
-
job: () => void | Promise<void>,
|
55
|
-
): { run: () => void; stop: () => void } {
|
56
|
-
let state: "sleeping" | "running" | "runningAndMarkedForRerun" | "stopped" =
|
57
|
-
"sleeping";
|
58
|
-
let timeout: any = undefined;
|
59
|
-
const scheduler = {
|
60
|
-
run: async () => {
|
61
|
-
switch (state) {
|
62
|
-
case "sleeping":
|
63
|
-
clearTimeout(timeout);
|
64
|
-
state = "running";
|
65
|
-
await job();
|
66
|
-
if (state === "running" || state === "runningAndMarkedForRerun") {
|
67
|
-
timeout = setTimeout(
|
68
|
-
() => {
|
69
|
-
scheduler.run();
|
70
|
-
},
|
71
|
-
(state as any) === "runningAndMarkedForRerun"
|
72
|
-
? 0
|
73
|
-
: interval + interval * intervalVariance * Math.random(),
|
74
|
-
);
|
75
|
-
state = "sleeping";
|
76
|
-
}
|
77
|
-
break;
|
78
|
-
case "running":
|
79
|
-
state = "runningAndMarkedForRerun";
|
80
|
-
break;
|
81
|
-
}
|
82
|
-
},
|
83
|
-
stop: () => {
|
84
|
-
clearTimeout(timeout);
|
85
|
-
state = "stopped";
|
86
|
-
process?.off?.("gracefulTermination", gracefulTerminationEventListener);
|
87
|
-
},
|
88
|
-
};
|
89
|
-
scheduler.run();
|
90
|
-
const gracefulTerminationEventListener = () => {
|
91
|
-
scheduler.stop();
|
92
|
-
};
|
93
|
-
process?.once?.("gracefulTermination", gracefulTerminationEventListener);
|
94
|
-
return scheduler;
|
95
|
-
}
|
96
|
-
|
97
1
|
/**
|
98
2
|
* A promisified version of `setTimeout()`. Bare-bones: It doesn’t even offer a way to `clearTimeout()`. Useful in JavaScript that may run in the browser—if you’re only targeting Node.js then you’re better served by [`timersPromises.setTimeout()`](https://nodejs.org/api/timers.html#timerspromisessettimeoutdelay-value-options).
|
99
3
|
*/
|
@@ -182,11 +86,6 @@ export const emailRegExp: RegExp = /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/i;
|
|
182
86
|
export const ISODateRegExp: RegExp =
|
183
87
|
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
|
184
88
|
|
185
|
-
/**
|
186
|
-
* A regular expression that matches localized dates, for example, `2024-04-01 15:20`.
|
187
|
-
*/
|
188
|
-
export const localizedDateRegExp: RegExp = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/;
|
189
|
-
|
190
89
|
/**
|
191
90
|
* Utility type for `intern()`.
|
192
91
|
*/
|
@@ -368,3 +267,89 @@ intern.finalizationRegistry = new FinalizationRegistry<{
|
|
368
267
|
}>(({ type, key }) => {
|
369
268
|
intern.pool[type].delete(key);
|
370
269
|
});
|
270
|
+
|
271
|
+
/**
|
272
|
+
* > **Note:** This is a lower level utility. See `@radically-straightforward/node`’s and `@radically-straightforward/javascript`’s extensions to `backgroundJob()` that are better suited for their specific environments.
|
273
|
+
*
|
274
|
+
* Start a background job that runs every `interval`.
|
275
|
+
*
|
276
|
+
* `backgroundJob()` is different from `setInterval()` in the following ways:
|
277
|
+
*
|
278
|
+
* 1. The interval counts **between** jobs, so slow background jobs don’t get called concurrently:
|
279
|
+
*
|
280
|
+
* ```
|
281
|
+
* setInterval()
|
282
|
+
* | SLOW BACKGROUND JOB |
|
283
|
+
* | INTERVAL | SLOW BACKGROUND JOB |
|
284
|
+
* | INTERVAL | ...
|
285
|
+
*
|
286
|
+
* backgroundJob()
|
287
|
+
* | SLOW BACKGROUND JOB | INTERVAL | SLOW BACKGROUND JOB | INTERVAL | ...
|
288
|
+
* ```
|
289
|
+
*
|
290
|
+
* 2. You may use `backgroundJob.run()` to force the background job to run right away. If the background job is already running, calling `backgroundJob.run()` schedules it to run again as soon as possible (with a wait interval of `0`).
|
291
|
+
*
|
292
|
+
* 3. You may use `backgroundJob.stop()` to stop the background job. If the background job is in the middle of running, it will finish but it will not be scheduled to run again. This is similar to how an HTTP server may terminate gracefully by stopping accepting new requests but finishing responding to existing requests. After a job has been stopped, you may not `backgroundJob.run()` it again (calling `backgroundJob.run()` has no effect).
|
293
|
+
*
|
294
|
+
* 4. We introduce a random interval variance of 10% on top of the given `interval` to avoid many background jobs from starting at the same time and overloading the machine.
|
295
|
+
*
|
296
|
+
* > **Note:** If the job throws an exception, the exception is logged and the background job continues.
|
297
|
+
*/
|
298
|
+
export function backgroundJob(
|
299
|
+
{
|
300
|
+
interval,
|
301
|
+
onStop = () => {},
|
302
|
+
}: {
|
303
|
+
interval: number;
|
304
|
+
onStop?: () => void | Promise<void>;
|
305
|
+
},
|
306
|
+
job: () => void | Promise<void>,
|
307
|
+
): {
|
308
|
+
run: () => Promise<void>;
|
309
|
+
stop: () => Promise<void>;
|
310
|
+
} {
|
311
|
+
let state: "sleeping" | "running" | "runningAndMarkedForRerun" | "stopped" =
|
312
|
+
"sleeping";
|
313
|
+
let timeout = setTimeout(() => {});
|
314
|
+
const backgroundJob = {
|
315
|
+
run: async () => {
|
316
|
+
switch (state) {
|
317
|
+
case "sleeping":
|
318
|
+
clearTimeout(timeout);
|
319
|
+
state = "running";
|
320
|
+
try {
|
321
|
+
await job();
|
322
|
+
} catch (error) {
|
323
|
+
log(
|
324
|
+
"BACKGROUND JOB ERROR",
|
325
|
+
String(error),
|
326
|
+
(error as Error)?.stack ?? "",
|
327
|
+
);
|
328
|
+
}
|
329
|
+
if (state === "running" || state === "runningAndMarkedForRerun") {
|
330
|
+
timeout = setTimeout(
|
331
|
+
() => {
|
332
|
+
backgroundJob.run();
|
333
|
+
},
|
334
|
+
(state as "running" | "runningAndMarkedForRerun") ===
|
335
|
+
"runningAndMarkedForRerun"
|
336
|
+
? 0
|
337
|
+
: interval * (1 + 0.1 * Math.random()),
|
338
|
+
);
|
339
|
+
state = "sleeping";
|
340
|
+
}
|
341
|
+
break;
|
342
|
+
case "running":
|
343
|
+
state = "runningAndMarkedForRerun";
|
344
|
+
break;
|
345
|
+
}
|
346
|
+
},
|
347
|
+
stop: async () => {
|
348
|
+
clearTimeout(timeout);
|
349
|
+
state = "stopped";
|
350
|
+
await onStop();
|
351
|
+
},
|
352
|
+
};
|
353
|
+
backgroundJob.run();
|
354
|
+
return backgroundJob;
|
355
|
+
}
|
package/source/index.test.mts
CHANGED
@@ -3,40 +3,6 @@ import assert from "node:assert/strict";
|
|
3
3
|
import * as utilities from "@radically-straightforward/utilities";
|
4
4
|
import { intern as $ } from "@radically-straightforward/utilities";
|
5
5
|
|
6
|
-
test(
|
7
|
-
"backgroundJob()",
|
8
|
-
{
|
9
|
-
skip: process.stdin.isTTY
|
10
|
-
? false
|
11
|
-
: "Run interactive test with ‘node ./build/index.test.mjs’.",
|
12
|
-
},
|
13
|
-
async () => {
|
14
|
-
for (let iteration = 0; iteration < 1000; iteration++) {
|
15
|
-
const backgroundJob = utilities.backgroundJob(
|
16
|
-
{ interval: 3 * 1000 },
|
17
|
-
() => {},
|
18
|
-
);
|
19
|
-
backgroundJob.stop();
|
20
|
-
// If background jobs leak ‘process.once("gracefulTermination")’ event listeners, then we get a warning in the console.
|
21
|
-
}
|
22
|
-
|
23
|
-
const backgroundJob = utilities.backgroundJob(
|
24
|
-
{ interval: 3 * 1000 },
|
25
|
-
async () => {
|
26
|
-
console.log("backgroundJob(): Running background job...");
|
27
|
-
await utilities.sleep(3 * 1000);
|
28
|
-
console.log("backgroundJob(): ...finished running background job.");
|
29
|
-
},
|
30
|
-
);
|
31
|
-
process.on("SIGTSTP", () => {
|
32
|
-
backgroundJob.run();
|
33
|
-
});
|
34
|
-
console.log(
|
35
|
-
"backgroundJob(): Press ⌃Z to force background job to run and ⌃C to gracefully terminate...",
|
36
|
-
);
|
37
|
-
},
|
38
|
-
);
|
39
|
-
|
40
6
|
test("sleep()", async () => {
|
41
7
|
const before = Date.now();
|
42
8
|
await utilities.sleep(1000);
|
@@ -105,14 +71,6 @@ test("ISODateRegExp", () => {
|
|
105
71
|
assert.doesNotMatch("2024-04-01 15:20", utilities.ISODateRegExp);
|
106
72
|
});
|
107
73
|
|
108
|
-
test("localizedDateRegExp", () => {
|
109
|
-
assert.match("2024-04-01 15:20", utilities.localizedDateRegExp);
|
110
|
-
assert.doesNotMatch(
|
111
|
-
"2024-04-01T14:19:48.162Z",
|
112
|
-
utilities.localizedDateRegExp,
|
113
|
-
);
|
114
|
-
});
|
115
|
-
|
116
74
|
test("intern()", () => {
|
117
75
|
// @ts-expect-error
|
118
76
|
assert(([1] === [1]) === false);
|
@@ -163,20 +121,34 @@ test("intern()", () => {
|
|
163
121
|
// @ts-expect-error
|
164
122
|
$([1])[0] = 2;
|
165
123
|
});
|
124
|
+
});
|
166
125
|
|
126
|
+
test(
|
127
|
+
"backgroundJob()",
|
167
128
|
{
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
129
|
+
skip: process.stdin.isTTY
|
130
|
+
? false
|
131
|
+
: "Run interactive test with ‘node ./build/index.test.mjs’.",
|
132
|
+
},
|
133
|
+
async () => {
|
134
|
+
const backgroundJob = utilities.backgroundJob(
|
135
|
+
{ interval: 3 * 1000 },
|
136
|
+
async () => {
|
137
|
+
console.log("backgroundJob(): Running background job...");
|
138
|
+
await utilities.sleep(3 * 1000);
|
139
|
+
console.log("backgroundJob(): ...finished running background job.");
|
140
|
+
if (Math.random() < 0.3)
|
141
|
+
throw new Error(
|
142
|
+
"There’s a 30% chance that the background job results in error.",
|
143
|
+
);
|
144
|
+
},
|
145
|
+
);
|
146
|
+
process.on("SIGTSTP", () => {
|
147
|
+
backgroundJob.run();
|
148
|
+
});
|
149
|
+
process.on("SIGINT", () => {
|
150
|
+
backgroundJob.stop();
|
151
|
+
});
|
152
|
+
console.log("backgroundJob(): Press ⌃Z to ‘run()’ and ⌃C to ‘stop()’...");
|
153
|
+
},
|
154
|
+
);
|
package/tsconfig.json
CHANGED