1 | 'use strict';
|
2 |
|
3 | const Emitter = require('events').EventEmitter;
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 | class EagerTimer extends Emitter {
|
10 | constructor(maxDelay) {
|
11 | super();
|
12 |
|
13 | if (!Number.isSafeInteger(maxDelay) || maxDelay <= 0) {
|
14 | throw new Error('maximum delay must be a positive integer');
|
15 | }
|
16 |
|
17 | this._maxDelay = maxDelay;
|
18 | this._nextTime = null;
|
19 | this._timer = null;
|
20 |
|
21 | this._stopped = false;
|
22 | this._boundTrigger = this._trigger.bind(this);
|
23 | }
|
24 |
|
25 | schedule(time) {
|
26 | if (this._stopped) return;
|
27 |
|
28 | const now = Date.now();
|
29 |
|
30 | if (time < 0 || time == null || isNaN(time)) {
|
31 |
|
32 | time = now + this._maxDelay;
|
33 | } else if (time <= now) {
|
34 |
|
35 | this._schedule(now + this._maxDelay);
|
36 | return this.emit('trigger');
|
37 | } else {
|
38 |
|
39 | time = Math.min(time, now + this._maxDelay);
|
40 | }
|
41 |
|
42 |
|
43 | if (!this._timer || time < this._nextTime) {
|
44 | this._schedule(time);
|
45 | }
|
46 | }
|
47 |
|
48 | stop() {
|
49 | this._stop();
|
50 | this._stopped = true;
|
51 | }
|
52 |
|
53 |
|
54 |
|
55 | _stop() {
|
56 | if (this._timer) {
|
57 | clearTimeout(this._timer);
|
58 | this._nextTime = null;
|
59 | this._timer = null;
|
60 | }
|
61 | }
|
62 |
|
63 | _schedule(time) {
|
64 | const duration = time - Date.now();
|
65 |
|
66 | this._stop();
|
67 | this._nextTime = time;
|
68 | this._timer = setTimeout(this._boundTrigger, duration);
|
69 | }
|
70 |
|
71 | _trigger() {
|
72 | const now = Date.now(), remaining = this._nextTime - now;
|
73 |
|
74 | if (remaining > 0) {
|
75 |
|
76 |
|
77 | this._timer = setTimeout(this._boundTrigger, remaining);
|
78 | return;
|
79 | }
|
80 | this._schedule(now + this._maxDelay);
|
81 | this.emit('trigger');
|
82 | }
|
83 | }
|
84 |
|
85 | module.exports = EagerTimer;
|