-
Notifications
You must be signed in to change notification settings - Fork 109
/
clock.js
99 lines (83 loc) · 3.13 KB
/
clock.js
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
89
90
91
92
93
94
95
96
97
98
// TODO rebase on observable
// A clock is an observable signal that emits a pulse with the current time
// with a given period and offset.
// The period is a number of miliseconds and the pulse will be emitted at times
// past the epoch that are multiples of that period, plus the offset
// miliseconds.
"use strict";
var Signal = require("./signal");
// ## Clock
//
// The clock constructor accepts the period and offsets in miliseconds, and
// allows you to optionally override the `now` function, which should provide
// the current time in miliseconds.
module.exports = Clock;
function Clock(options) {
options = options || {};
var handler = new this.Handler(options.period, options.offset, options.now);
Signal.prototype.Observable.call(this, handler);
}
// Clock inherits exclusively from the observable side of a signal and
// generates values internally.
Clock.prototype = Object.create(Signal.prototype.Observable.prototype);
Clock.prototype.constructor = Clock;
Clock.prototype.Handler = ClockHandler;
// ## ClockHandler
//
// The clock handler inherits the utilities of a normal signal handler, but
// also does some book keeping to schedule timers, activate, and deactivate.
function ClockHandler(period, offset, now) {
Signal.prototype.Handler.call(this);
this.next = null;
this.period = period || 1000;
this.offset = offset || 0;
this.now = now || this.now;
this.value = this.now();
this.timeout = null;
// We use the tock method as a timeout handler, so it has to be bound.
this.tock = this.tock.bind(this);
}
ClockHandler.prototype = Object.create(Signal.prototype.Handler.prototype);
ClockHandler.prototype.constructor = ClockHandler;
ClockHandler.prototype.now = Date.now;
// The `tick` method arranges for a `tock` method call at the next time that
// aligns with the period and offset.
// Note that a clock may miss beats if the process spends too much time between
// events.
ClockHandler.prototype.tick = function (now) {
if (this.next === null) {
this.next = Math.ceil(
Math.floor(
(now - this.offset) / this.period
) + .5
) * this.period + this.offset;
this.timeout = setTimeout(this.tock, this.next - now);
}
};
// The `tock` method responds to timers and propagates the current time, both
// as the pulse value and time index.
ClockHandler.prototype.tock = function () {
this.next = null;
var now = this.now();
this.yield(now, now);
if (this.active) {
this.tick(now);
}
};
ClockHandler.prototype.timer = true;
// The signal handler arranges for active and inactive state changes by calling
// either `onstart` or `onstop` depending on whether there are any observers
// registered.
// When the clock becomes active, we kick off an initial tick.
// Each subsequent tick is scheduled by the `tock` method if the clock remains
// active.
ClockHandler.prototype.onstart = function () {
this.tick(this.value);
};
// The `onstop` method cancels the next `tick`, breaking the chain of scheduled
// pulses.
ClockHandler.prototype.onstop = function () {
if (this.timeout) {
clearTimeout(this.timeout);
}
};