UNPKG

2.11 kBJavaScriptView Raw
1'use strict';
2
3const Emitter = require('events').EventEmitter;
4
5/**
6 * A timer that will eagerly replace an existing timer with a sooner one.
7 * Refuses to set a later timer, or a timer beyond the given maximum delay.
8 */
9class 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 // Times earlier than 0 signify maximum delay.
32 time = now + this._maxDelay;
33 } else if (time <= now) {
34 // If it's in the past, trigger immediately, and reschedule to max delay.
35 this._schedule(now + this._maxDelay);
36 return this.emit('trigger');
37 } else {
38 // Don't try to schedule past the maximum delay.
39 time = Math.min(time, now + this._maxDelay);
40 }
41
42 // Only overwrite the existing timer if later than the given time.
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 // PRIVATE METHODS
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 /* istanbul ignore if */
74 if (remaining > 0) {
75 // It's possible (it caused tests to fail) to have the timeout trigger
76 // before the scheduled time.
77 this._timer = setTimeout(this._boundTrigger, remaining);
78 return;
79 }
80 this._schedule(now + this._maxDelay);
81 this.emit('trigger');
82 }
83}
84
85module.exports = EagerTimer;