timers.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. 'use strict';
  5. module.metadata = {
  6. "stability": "stable"
  7. };
  8. const { CC, Cc, Ci } = require("chrome");
  9. const { when: unload } = require("./system/unload");
  10. const { TYPE_ONE_SHOT, TYPE_REPEATING_SLACK } = Ci.nsITimer;
  11. const Timer = CC("@mozilla.org/timer;1", "nsITimer");
  12. const timers = Object.create(null);
  13. const threadManager = Cc["@mozilla.org/thread-manager;1"].
  14. getService(Ci.nsIThreadManager);
  15. const prefBranch = Cc["@mozilla.org/preferences-service;1"].
  16. getService(Ci.nsIPrefService).
  17. QueryInterface(Ci.nsIPrefBranch);
  18. let MIN_DELAY = 4;
  19. // Try to get min timeout delay used by browser.
  20. try { MIN_DELAY = prefBranch.getIntPref("dom.min_timeout_value"); } finally {}
  21. // Last timer id.
  22. let lastID = 0;
  23. // Sets typer either by timeout or by interval
  24. // depending on a given type.
  25. function setTimer(type, callback, delay, ...args) {
  26. let id = ++ lastID;
  27. let timer = timers[id] = Timer();
  28. timer.initWithCallback({
  29. notify: function notify() {
  30. try {
  31. if (type === TYPE_ONE_SHOT)
  32. delete timers[id];
  33. callback.apply(null, args);
  34. }
  35. catch(error) {
  36. console.exception(error);
  37. }
  38. }
  39. }, Math.max(delay || MIN_DELAY), type);
  40. return id;
  41. }
  42. function unsetTimer(id) {
  43. let timer = timers[id];
  44. delete timers[id];
  45. if (timer) timer.cancel();
  46. }
  47. let immediates = new Map();
  48. let dispatcher = _ => {
  49. // Allow scheduling of a new dispatch loop.
  50. dispatcher.scheduled = false;
  51. // Take a snapshot of timer `id`'s that have being present before
  52. // starting a dispatch loop, in order to ignore timers registered
  53. // in side effect to dispatch while also skipping immediates that
  54. // were removed in side effect.
  55. let ids = [id for ([id] of immediates)];
  56. for (let id of ids) {
  57. let immediate = immediates.get(id);
  58. if (immediate) {
  59. immediates.delete(id);
  60. try { immediate(); }
  61. catch (error) { console.exception(error); }
  62. }
  63. }
  64. }
  65. function setImmediate(callback, ...params) {
  66. let id = ++ lastID;
  67. // register new immediate timer with curried params.
  68. immediates.set(id, _ => callback.apply(callback, params));
  69. // if dispatch loop is not scheduled schedule one. Own scheduler
  70. if (!dispatcher.scheduled) {
  71. dispatcher.scheduled = true;
  72. threadManager.currentThread.dispatch(dispatcher,
  73. Ci.nsIThread.DISPATCH_NORMAL);
  74. }
  75. return id;
  76. }
  77. function clearImmediate(id) {
  78. immediates.delete(id);
  79. }
  80. // Bind timers so that toString-ing them looks same as on native timers.
  81. exports.setImmediate = setImmediate.bind(null);
  82. exports.clearImmediate = clearImmediate.bind(null);
  83. exports.setTimeout = setTimer.bind(null, TYPE_ONE_SHOT);
  84. exports.setInterval = setTimer.bind(null, TYPE_REPEATING_SLACK);
  85. exports.clearTimeout = unsetTimer.bind(null);
  86. exports.clearInterval = unsetTimer.bind(null);
  87. // all timers are cleared out on unload.
  88. unload(function() {
  89. immediates.clear();
  90. Object.keys(timers).forEach(unsetTimer)
  91. });