rx.virtualtime.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. // Copyright (c) Microsoft, All rights reserved. See License.txt in the project root for license information.
  2. ;(function (factory) {
  3. var objectTypes = {
  4. 'function': true,
  5. 'object': true
  6. };
  7. function checkGlobal(value) {
  8. return (value && value.Object === Object) ? value : null;
  9. }
  10. var freeExports = (objectTypes[typeof exports] && exports && !exports.nodeType) ? exports : null;
  11. var freeModule = (objectTypes[typeof module] && module && !module.nodeType) ? module : null;
  12. var freeGlobal = checkGlobal(freeExports && freeModule && typeof global === 'object' && global);
  13. var freeSelf = checkGlobal(objectTypes[typeof self] && self);
  14. var freeWindow = checkGlobal(objectTypes[typeof window] && window);
  15. var moduleExports = (freeModule && freeModule.exports === freeExports) ? freeExports : null;
  16. var thisGlobal = checkGlobal(objectTypes[typeof this] && this);
  17. var root = freeGlobal || ((freeWindow !== (thisGlobal && thisGlobal.window)) && freeWindow) || freeSelf || thisGlobal || Function('return this')();
  18. // Because of build optimizers
  19. if (typeof define === 'function' && define.amd) {
  20. define(['./rx'], function (Rx, exports) {
  21. return factory(root, exports, Rx);
  22. });
  23. } else if (typeof module === 'object' && module && module.exports === freeExports) {
  24. module.exports = factory(root, module.exports, require('./rx'));
  25. } else {
  26. root.Rx = factory(root, {}, root.Rx);
  27. }
  28. }.call(this, function (root, exp, Rx, undefined) {
  29. // Aliases
  30. var Scheduler = Rx.Scheduler,
  31. ScheduledItem = Rx.internals.ScheduledItem,
  32. SchedulePeriodicRecursive = Rx.internals.SchedulePeriodicRecursive,
  33. PriorityQueue = Rx.internals.PriorityQueue,
  34. inherits = Rx.internals.inherits,
  35. defaultSubComparer = Rx.helpers.defaultSubComparer,
  36. notImplemented = Rx.helpers.notImplemented;
  37. /** Provides a set of extension methods for virtual time scheduling. */
  38. var VirtualTimeScheduler = Rx.VirtualTimeScheduler = (function (__super__) {
  39. inherits(VirtualTimeScheduler, __super__);
  40. /**
  41. * Creates a new virtual time scheduler with the specified initial clock value and absolute time comparer.
  42. *
  43. * @constructor
  44. * @param {Number} initialClock Initial value for the clock.
  45. * @param {Function} comparer Comparer to determine causality of events based on absolute time.
  46. */
  47. function VirtualTimeScheduler(initialClock, comparer) {
  48. this.clock = initialClock;
  49. this.comparer = comparer;
  50. this.isEnabled = false;
  51. this.queue = new PriorityQueue(1024);
  52. __super__.call(this);
  53. }
  54. var VirtualTimeSchedulerPrototype = VirtualTimeScheduler.prototype;
  55. VirtualTimeSchedulerPrototype.now = function () {
  56. return this.toAbsoluteTime(this.clock);
  57. };
  58. VirtualTimeSchedulerPrototype.schedule = function (state, action) {
  59. return this.scheduleAbsolute(state, this.clock, action);
  60. };
  61. VirtualTimeSchedulerPrototype.scheduleFuture = function (state, dueTime, action) {
  62. var dt = dueTime instanceof Date ?
  63. this.toRelativeTime(dueTime - this.now()) :
  64. this.toRelativeTime(dueTime);
  65. return this.scheduleRelative(state, dt, action);
  66. };
  67. /**
  68. * Adds a relative time value to an absolute time value.
  69. * @param {Number} absolute Absolute virtual time value.
  70. * @param {Number} relative Relative virtual time value to add.
  71. * @return {Number} Resulting absolute virtual time sum value.
  72. */
  73. VirtualTimeSchedulerPrototype.add = notImplemented;
  74. /**
  75. * Converts an absolute time to a number
  76. * @param {Any} The absolute time.
  77. * @returns {Number} The absolute time in ms
  78. */
  79. VirtualTimeSchedulerPrototype.toAbsoluteTime = notImplemented;
  80. /**
  81. * Converts the TimeSpan value to a relative virtual time value.
  82. * @param {Number} timeSpan TimeSpan value to convert.
  83. * @return {Number} Corresponding relative virtual time value.
  84. */
  85. VirtualTimeSchedulerPrototype.toRelativeTime = notImplemented;
  86. /**
  87. * Schedules a periodic piece of work by dynamically discovering the scheduler's capabilities. The periodic task will be emulated using recursive scheduling.
  88. * @param {Mixed} state Initial state passed to the action upon the first iteration.
  89. * @param {Number} period Period for running the work periodically.
  90. * @param {Function} action Action to be executed, potentially updating the state.
  91. * @returns {Disposable} The disposable object used to cancel the scheduled recurring action (best effort).
  92. */
  93. VirtualTimeSchedulerPrototype.schedulePeriodic = function (state, period, action) {
  94. var s = new SchedulePeriodicRecursive(this, state, period, action);
  95. return s.start();
  96. };
  97. /**
  98. * Schedules an action to be executed after dueTime.
  99. * @param {Mixed} state State passed to the action to be executed.
  100. * @param {Number} dueTime Relative time after which to execute the action.
  101. * @param {Function} action Action to be executed.
  102. * @returns {Disposable} The disposable object used to cancel the scheduled action (best effort).
  103. */
  104. VirtualTimeSchedulerPrototype.scheduleRelative = function (state, dueTime, action) {
  105. var runAt = this.add(this.clock, dueTime);
  106. return this.scheduleAbsolute(state, runAt, action);
  107. };
  108. /**
  109. * Starts the virtual time scheduler.
  110. */
  111. VirtualTimeSchedulerPrototype.start = function () {
  112. if (!this.isEnabled) {
  113. this.isEnabled = true;
  114. do {
  115. var next = this.getNext();
  116. if (next !== null) {
  117. this.comparer(next.dueTime, this.clock) > 0 && (this.clock = next.dueTime);
  118. next.invoke();
  119. } else {
  120. this.isEnabled = false;
  121. }
  122. } while (this.isEnabled);
  123. }
  124. };
  125. /**
  126. * Stops the virtual time scheduler.
  127. */
  128. VirtualTimeSchedulerPrototype.stop = function () {
  129. this.isEnabled = false;
  130. };
  131. /**
  132. * Advances the scheduler's clock to the specified time, running all work till that point.
  133. * @param {Number} time Absolute time to advance the scheduler's clock to.
  134. */
  135. VirtualTimeSchedulerPrototype.advanceTo = function (time) {
  136. var dueToClock = this.comparer(this.clock, time);
  137. if (this.comparer(this.clock, time) > 0) { throw new ArgumentOutOfRangeError(); }
  138. if (dueToClock === 0) { return; }
  139. if (!this.isEnabled) {
  140. this.isEnabled = true;
  141. do {
  142. var next = this.getNext();
  143. if (next !== null && this.comparer(next.dueTime, time) <= 0) {
  144. this.comparer(next.dueTime, this.clock) > 0 && (this.clock = next.dueTime);
  145. next.invoke();
  146. } else {
  147. this.isEnabled = false;
  148. }
  149. } while (this.isEnabled);
  150. this.clock = time;
  151. }
  152. };
  153. /**
  154. * Advances the scheduler's clock by the specified relative time, running all work scheduled for that timespan.
  155. * @param {Number} time Relative time to advance the scheduler's clock by.
  156. */
  157. VirtualTimeSchedulerPrototype.advanceBy = function (time) {
  158. var dt = this.add(this.clock, time),
  159. dueToClock = this.comparer(this.clock, dt);
  160. if (dueToClock > 0) { throw new ArgumentOutOfRangeError(); }
  161. if (dueToClock === 0) { return; }
  162. this.advanceTo(dt);
  163. };
  164. /**
  165. * Advances the scheduler's clock by the specified relative time.
  166. * @param {Number} time Relative time to advance the scheduler's clock by.
  167. */
  168. VirtualTimeSchedulerPrototype.sleep = function (time) {
  169. var dt = this.add(this.clock, time);
  170. if (this.comparer(this.clock, dt) >= 0) { throw new ArgumentOutOfRangeError(); }
  171. this.clock = dt;
  172. };
  173. /**
  174. * Gets the next scheduled item to be executed.
  175. * @returns {ScheduledItem} The next scheduled item.
  176. */
  177. VirtualTimeSchedulerPrototype.getNext = function () {
  178. while (this.queue.length > 0) {
  179. var next = this.queue.peek();
  180. if (next.isCancelled()) {
  181. this.queue.dequeue();
  182. } else {
  183. return next;
  184. }
  185. }
  186. return null;
  187. };
  188. /**
  189. * Schedules an action to be executed at dueTime.
  190. * @param {Mixed} state State passed to the action to be executed.
  191. * @param {Number} dueTime Absolute time at which to execute the action.
  192. * @param {Function} action Action to be executed.
  193. * @returns {Disposable} The disposable object used to cancel the scheduled action (best effort).
  194. */
  195. VirtualTimeSchedulerPrototype.scheduleAbsolute = function (state, dueTime, action) {
  196. var self = this;
  197. function run(scheduler, state1) {
  198. self.queue.remove(si);
  199. return action(scheduler, state1);
  200. }
  201. var si = new ScheduledItem(this, state, run, dueTime, this.comparer);
  202. this.queue.enqueue(si);
  203. return si.disposable;
  204. };
  205. return VirtualTimeScheduler;
  206. }(Scheduler));
  207. /** Provides a virtual time scheduler that uses Date for absolute time and number for relative time. */
  208. Rx.HistoricalScheduler = (function (__super__) {
  209. inherits(HistoricalScheduler, __super__);
  210. /**
  211. * Creates a new historical scheduler with the specified initial clock value.
  212. * @constructor
  213. * @param {Number} initialClock Initial value for the clock.
  214. * @param {Function} comparer Comparer to determine causality of events based on absolute time.
  215. */
  216. function HistoricalScheduler(initialClock, comparer) {
  217. var clock = initialClock == null ? 0 : initialClock;
  218. var cmp = comparer || defaultSubComparer;
  219. __super__.call(this, clock, cmp);
  220. }
  221. var HistoricalSchedulerProto = HistoricalScheduler.prototype;
  222. /**
  223. * Adds a relative time value to an absolute time value.
  224. * @param {Number} absolute Absolute virtual time value.
  225. * @param {Number} relative Relative virtual time value to add.
  226. * @return {Number} Resulting absolute virtual time sum value.
  227. */
  228. HistoricalSchedulerProto.add = function (absolute, relative) {
  229. return absolute + relative;
  230. };
  231. HistoricalSchedulerProto.toAbsoluteTime = function (absolute) {
  232. return new Date(absolute).getTime();
  233. };
  234. /**
  235. * Converts the TimeSpan value to a relative virtual time value.
  236. * @memberOf HistoricalScheduler
  237. * @param {Number} timeSpan TimeSpan value to convert.
  238. * @return {Number} Corresponding relative virtual time value.
  239. */
  240. HistoricalSchedulerProto.toRelativeTime = function (timeSpan) {
  241. return timeSpan;
  242. };
  243. return HistoricalScheduler;
  244. }(Rx.VirtualTimeScheduler));
  245. return Rx;
  246. }));