timer.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import { Observable } from "../Misc/observable.js";
  2. /**
  3. * The current state of the timer
  4. */
  5. export var TimerState;
  6. (function (TimerState) {
  7. /**
  8. * Timer initialized, not yet started
  9. */
  10. TimerState[TimerState["INIT"] = 0] = "INIT";
  11. /**
  12. * Timer started and counting
  13. */
  14. TimerState[TimerState["STARTED"] = 1] = "STARTED";
  15. /**
  16. * Timer ended (whether aborted or time reached)
  17. */
  18. TimerState[TimerState["ENDED"] = 2] = "ENDED";
  19. })(TimerState || (TimerState = {}));
  20. /**
  21. * A simple version of the timer. Will take options and start the timer immediately after calling it
  22. *
  23. * @param options options with which to initialize this timer
  24. * @returns an observer that can be used to stop the timer
  25. */
  26. export function setAndStartTimer(options) {
  27. let timer = 0;
  28. const startTime = Date.now();
  29. options.observableParameters = options.observableParameters ?? {};
  30. const observer = options.contextObservable.add((payload) => {
  31. const now = Date.now();
  32. timer = now - startTime;
  33. const data = {
  34. startTime,
  35. currentTime: now,
  36. deltaTime: timer,
  37. completeRate: timer / options.timeout,
  38. payload,
  39. };
  40. options.onTick && options.onTick(data);
  41. if (options.breakCondition && options.breakCondition()) {
  42. options.contextObservable.remove(observer);
  43. options.onAborted && options.onAborted(data);
  44. }
  45. if (timer >= options.timeout) {
  46. options.contextObservable.remove(observer);
  47. options.onEnded && options.onEnded(data);
  48. }
  49. }, options.observableParameters.mask, options.observableParameters.insertFirst, options.observableParameters.scope);
  50. return observer;
  51. }
  52. /**
  53. * An advanced implementation of a timer class
  54. */
  55. export class AdvancedTimer {
  56. /**
  57. * Will construct a new advanced timer based on the options provided. Timer will not start until start() is called.
  58. * @param options construction options for this advanced timer
  59. */
  60. constructor(options) {
  61. /**
  62. * Will notify each time the timer calculates the remaining time
  63. */
  64. this.onEachCountObservable = new Observable();
  65. /**
  66. * Will trigger when the timer was aborted due to the break condition
  67. */
  68. this.onTimerAbortedObservable = new Observable();
  69. /**
  70. * Will trigger when the timer ended successfully
  71. */
  72. this.onTimerEndedObservable = new Observable();
  73. /**
  74. * Will trigger when the timer state has changed
  75. */
  76. this.onStateChangedObservable = new Observable();
  77. this._observer = null;
  78. this._breakOnNextTick = false;
  79. this._tick = (payload) => {
  80. const now = Date.now();
  81. this._timer = now - this._startTime;
  82. const data = {
  83. startTime: this._startTime,
  84. currentTime: now,
  85. deltaTime: this._timer,
  86. completeRate: this._timer / this._timeToEnd,
  87. payload,
  88. };
  89. const shouldBreak = this._breakOnNextTick || this._breakCondition(data);
  90. if (shouldBreak || this._timer >= this._timeToEnd) {
  91. this._stop(data, shouldBreak);
  92. }
  93. else {
  94. this.onEachCountObservable.notifyObservers(data);
  95. }
  96. };
  97. this._setState(TimerState.INIT);
  98. this._contextObservable = options.contextObservable;
  99. this._observableParameters = options.observableParameters ?? {};
  100. this._breakCondition = options.breakCondition ?? (() => false);
  101. this._timeToEnd = options.timeout;
  102. if (options.onEnded) {
  103. this.onTimerEndedObservable.add(options.onEnded);
  104. }
  105. if (options.onTick) {
  106. this.onEachCountObservable.add(options.onTick);
  107. }
  108. if (options.onAborted) {
  109. this.onTimerAbortedObservable.add(options.onAborted);
  110. }
  111. }
  112. /**
  113. * set a breaking condition for this timer. Default is to never break during count
  114. * @param predicate the new break condition. Returns true to break, false otherwise
  115. */
  116. set breakCondition(predicate) {
  117. this._breakCondition = predicate;
  118. }
  119. /**
  120. * Reset ALL associated observables in this advanced timer
  121. */
  122. clearObservables() {
  123. this.onEachCountObservable.clear();
  124. this.onTimerAbortedObservable.clear();
  125. this.onTimerEndedObservable.clear();
  126. this.onStateChangedObservable.clear();
  127. }
  128. /**
  129. * Will start a new iteration of this timer. Only one instance of this timer can run at a time.
  130. *
  131. * @param timeToEnd how much time to measure until timer ended
  132. */
  133. start(timeToEnd = this._timeToEnd) {
  134. if (this._state === TimerState.STARTED) {
  135. throw new Error("Timer already started. Please stop it before starting again");
  136. }
  137. this._timeToEnd = timeToEnd;
  138. this._startTime = Date.now();
  139. this._timer = 0;
  140. this._observer = this._contextObservable.add(this._tick, this._observableParameters.mask, this._observableParameters.insertFirst, this._observableParameters.scope);
  141. this._setState(TimerState.STARTED);
  142. }
  143. /**
  144. * Will force a stop on the next tick.
  145. */
  146. stop() {
  147. if (this._state !== TimerState.STARTED) {
  148. return;
  149. }
  150. this._breakOnNextTick = true;
  151. }
  152. /**
  153. * Dispose this timer, clearing all resources
  154. */
  155. dispose() {
  156. if (this._observer) {
  157. this._contextObservable.remove(this._observer);
  158. }
  159. this.clearObservables();
  160. }
  161. _setState(newState) {
  162. this._state = newState;
  163. this.onStateChangedObservable.notifyObservers(this._state);
  164. }
  165. _stop(data, aborted = false) {
  166. this._contextObservable.remove(this._observer);
  167. this._setState(TimerState.ENDED);
  168. if (aborted) {
  169. this.onTimerAbortedObservable.notifyObservers(data);
  170. }
  171. else {
  172. this.onTimerEndedObservable.notifyObservers(data);
  173. }
  174. }
  175. }
  176. //# sourceMappingURL=timer.js.map