forked from video-dev/hls.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
task-loop.ts
127 lines (116 loc) · 3.99 KB
/
task-loop.ts
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/**
* Sub-class specialization of EventHandler base class.
*
* TaskLoop allows to schedule a task function being called (optionnaly repeatedly) on the main loop,
* scheduled asynchroneously, avoiding recursive calls in the same tick.
*
* The task itself is implemented in `doTick`. It can be requested and called for single execution
* using the `tick` method.
*
* It will be assured that the task execution method (`tick`) only gets called once per main loop "tick",
* no matter how often it gets requested for execution. Execution in further ticks will be scheduled accordingly.
*
* If further execution requests have already been scheduled on the next tick, it can be checked with `hasNextTick`,
* and cancelled with `clearNextTick`.
*
* The task can be scheduled as an interval repeatedly with a period as parameter (see `setInterval`, `clearInterval`).
*
* Sub-classes need to implement the `doTick` method which will effectively have the task execution routine.
*
* Further explanations:
*
* The baseclass has a `tick` method that will schedule the doTick call. It may be called synchroneously
* only for a stack-depth of one. On re-entrant calls, sub-sequent calls are scheduled for next main loop ticks.
*
* When the task execution (`tick` method) is called in re-entrant way this is detected and
* we are limiting the task execution per call stack to exactly one, but scheduling/post-poning further
* task processing on the next main loop iteration (also known as "next tick" in the Node/JS runtime lingo).
*/
export default class TaskLoop {
private readonly _boundTick: () => void;
private _tickTimer: number | null = null;
private _tickInterval: number | null = null;
private _tickCallCount = 0;
constructor() {
this._boundTick = this.tick.bind(this);
}
public destroy() {
this.onHandlerDestroying();
this.onHandlerDestroyed();
}
protected onHandlerDestroying() {
// clear all timers before unregistering from event bus
this.clearNextTick();
this.clearInterval();
}
protected onHandlerDestroyed() {}
/**
* @returns {boolean}
*/
public hasInterval(): boolean {
return !!this._tickInterval;
}
/**
* @returns {boolean}
*/
public hasNextTick(): boolean {
return !!this._tickTimer;
}
/**
* @param {number} millis Interval time (ms)
* @returns {boolean} True when interval has been scheduled, false when already scheduled (no effect)
*/
public setInterval(millis: number): boolean {
if (!this._tickInterval) {
this._tickInterval = self.setInterval(this._boundTick, millis);
return true;
}
return false;
}
/**
* @returns {boolean} True when interval was cleared, false when none was set (no effect)
*/
public clearInterval(): boolean {
if (this._tickInterval) {
self.clearInterval(this._tickInterval);
this._tickInterval = null;
return true;
}
return false;
}
/**
* @returns {boolean} True when timeout was cleared, false when none was set (no effect)
*/
public clearNextTick(): boolean {
if (this._tickTimer) {
self.clearTimeout(this._tickTimer);
this._tickTimer = null;
return true;
}
return false;
}
/**
* Will call the subclass doTick implementation in this main loop tick
* or in the next one (via setTimeout(,0)) in case it has already been called
* in this tick (in case this is a re-entrant call).
*/
public tick(): void {
this._tickCallCount++;
if (this._tickCallCount === 1) {
this.doTick();
// re-entrant call to tick from previous doTick call stack
// -> schedule a call on the next main loop iteration to process this task processing request
if (this._tickCallCount > 1) {
// make sure only one timer exists at any time at max
this.clearNextTick();
this._tickTimer = self.setTimeout(this._boundTick, 0);
}
this._tickCallCount = 0;
}
}
/**
* For subclass to implement task logic
* @abstract
*/
protected doTick(): void {}
}