Home Reference Source

src/task-loop.ts

  1. import EventHandler from './event-handler';
  2. import Hls from './hls';
  3.  
  4. /**
  5. * Sub-class specialization of EventHandler base class.
  6. *
  7. * TaskLoop allows to schedule a task function being called (optionnaly repeatedly) on the main loop,
  8. * scheduled asynchroneously, avoiding recursive calls in the same tick.
  9. *
  10. * The task itself is implemented in `doTick`. It can be requested and called for single execution
  11. * using the `tick` method.
  12. *
  13. * It will be assured that the task execution method (`tick`) only gets called once per main loop "tick",
  14. * no matter how often it gets requested for execution. Execution in further ticks will be scheduled accordingly.
  15. *
  16. * If further execution requests have already been scheduled on the next tick, it can be checked with `hasNextTick`,
  17. * and cancelled with `clearNextTick`.
  18. *
  19. * The task can be scheduled as an interval repeatedly with a period as parameter (see `setInterval`, `clearInterval`).
  20. *
  21. * Sub-classes need to implement the `doTick` method which will effectively have the task execution routine.
  22. *
  23. * Further explanations:
  24. *
  25. * The baseclass has a `tick` method that will schedule the doTick call. It may be called synchroneously
  26. * only for a stack-depth of one. On re-entrant calls, sub-sequent calls are scheduled for next main loop ticks.
  27. *
  28. * When the task execution (`tick` method) is called in re-entrant way this is detected and
  29. * we are limiting the task execution per call stack to exactly one, but scheduling/post-poning further
  30. * task processing on the next main loop iteration (also known as "next tick" in the Node/JS runtime lingo).
  31. */
  32.  
  33. export default class TaskLoop extends EventHandler {
  34. private readonly _boundTick: () => void;
  35. private _tickTimer: number | null = null;
  36. private _tickInterval: number | null = null;
  37. private _tickCallCount = 0;
  38.  
  39. constructor (hls: Hls, ...events: string[]) {
  40. super(hls, ...events);
  41. this._boundTick = this.tick.bind(this);
  42. }
  43.  
  44. /**
  45. * @override
  46. */
  47. protected onHandlerDestroying () {
  48. // clear all timers before unregistering from event bus
  49. this.clearNextTick();
  50. this.clearInterval();
  51. }
  52.  
  53. /**
  54. * @returns {boolean}
  55. */
  56. public hasInterval (): boolean {
  57. return !!this._tickInterval;
  58. }
  59.  
  60. /**
  61. * @returns {boolean}
  62. */
  63. public hasNextTick (): boolean {
  64. return !!this._tickTimer;
  65. }
  66.  
  67. /**
  68. * @param {number} millis Interval time (ms)
  69. * @returns {boolean} True when interval has been scheduled, false when already scheduled (no effect)
  70. */
  71. public setInterval (millis: number): boolean {
  72. if (!this._tickInterval) {
  73. this._tickInterval = self.setInterval(this._boundTick, millis);
  74. return true;
  75. }
  76. return false;
  77. }
  78.  
  79. /**
  80. * @returns {boolean} True when interval was cleared, false when none was set (no effect)
  81. */
  82. public clearInterval (): boolean {
  83. if (this._tickInterval) {
  84. self.clearInterval(this._tickInterval);
  85. this._tickInterval = null;
  86. return true;
  87. }
  88. return false;
  89. }
  90.  
  91. /**
  92. * @returns {boolean} True when timeout was cleared, false when none was set (no effect)
  93. */
  94. public clearNextTick (): boolean {
  95. if (this._tickTimer) {
  96. self.clearTimeout(this._tickTimer);
  97. this._tickTimer = null;
  98. return true;
  99. }
  100. return false;
  101. }
  102.  
  103. /**
  104. * Will call the subclass doTick implementation in this main loop tick
  105. * or in the next one (via setTimeout(,0)) in case it has already been called
  106. * in this tick (in case this is a re-entrant call).
  107. */
  108. public tick (): void {
  109. this._tickCallCount++;
  110. if (this._tickCallCount === 1) {
  111. this.doTick();
  112. // re-entrant call to tick from previous doTick call stack
  113. // -> schedule a call on the next main loop iteration to process this task processing request
  114. if (this._tickCallCount > 1) {
  115. // make sure only one timer exists at any time at max
  116. this.clearNextTick();
  117. this._tickTimer = self.setTimeout(this._boundTick, 0);
  118. }
  119. this._tickCallCount = 0;
  120. }
  121. }
  122.  
  123. /**
  124. * For subclass to implement task logic
  125. * @abstract
  126. */
  127. protected doTick (): void {}
  128. }