setImmediate.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. (function (global, undefined) {
  2. "use strict";
  3. if (global.setImmediate) {
  4. return;
  5. }
  6. var nextHandle = 1; // Spec says greater than zero
  7. var tasksByHandle = {};
  8. var currentlyRunningATask = false;
  9. var doc = global.document;
  10. var registerImmediate;
  11. function setImmediate(callback) {
  12. // Callback can either be a function or a string
  13. if (typeof callback !== "function") {
  14. callback = new Function("" + callback);
  15. }
  16. // Copy function arguments
  17. var args = new Array(arguments.length - 1);
  18. for (var i = 0; i < args.length; i++) {
  19. args[i] = arguments[i + 1];
  20. }
  21. // Store and register the task
  22. var task = { callback: callback, args: args };
  23. tasksByHandle[nextHandle] = task;
  24. registerImmediate(nextHandle);
  25. return nextHandle++;
  26. }
  27. function clearImmediate(handle) {
  28. delete tasksByHandle[handle];
  29. }
  30. function run(task) {
  31. var callback = task.callback;
  32. var args = task.args;
  33. switch (args.length) {
  34. case 0:
  35. callback();
  36. break;
  37. case 1:
  38. callback(args[0]);
  39. break;
  40. case 2:
  41. callback(args[0], args[1]);
  42. break;
  43. case 3:
  44. callback(args[0], args[1], args[2]);
  45. break;
  46. default:
  47. callback.apply(undefined, args);
  48. break;
  49. }
  50. }
  51. function runIfPresent(handle) {
  52. // From the spec: "Wait until any invocations of this algorithm started before this one have completed."
  53. // So if we're currently running a task, we'll need to delay this invocation.
  54. if (currentlyRunningATask) {
  55. // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a
  56. // "too much recursion" error.
  57. setTimeout(runIfPresent, 0, handle);
  58. } else {
  59. var task = tasksByHandle[handle];
  60. if (task) {
  61. currentlyRunningATask = true;
  62. try {
  63. run(task);
  64. } finally {
  65. clearImmediate(handle);
  66. currentlyRunningATask = false;
  67. }
  68. }
  69. }
  70. }
  71. function installNextTickImplementation() {
  72. registerImmediate = function(handle) {
  73. process.nextTick(function () { runIfPresent(handle); });
  74. };
  75. }
  76. function canUsePostMessage() {
  77. // The test against `importScripts` prevents this implementation from being installed inside a web worker,
  78. // where `global.postMessage` means something completely different and can't be used for this purpose.
  79. if (global.postMessage && !global.importScripts) {
  80. var postMessageIsAsynchronous = true;
  81. var oldOnMessage = global.onmessage;
  82. global.onmessage = function() {
  83. postMessageIsAsynchronous = false;
  84. };
  85. global.postMessage("", "*");
  86. global.onmessage = oldOnMessage;
  87. return postMessageIsAsynchronous;
  88. }
  89. }
  90. function installPostMessageImplementation() {
  91. // Installs an event handler on `global` for the `message` event: see
  92. // * https://developer.mozilla.org/en/DOM/window.postMessage
  93. // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages
  94. var messagePrefix = "setImmediate$" + Math.random() + "$";
  95. var onGlobalMessage = function(event) {
  96. if (event.source === global &&
  97. typeof event.data === "string" &&
  98. event.data.indexOf(messagePrefix) === 0) {
  99. runIfPresent(+event.data.slice(messagePrefix.length));
  100. }
  101. };
  102. if (global.addEventListener) {
  103. global.addEventListener("message", onGlobalMessage, false);
  104. } else {
  105. global.attachEvent("onmessage", onGlobalMessage);
  106. }
  107. registerImmediate = function(handle) {
  108. global.postMessage(messagePrefix + handle, "*");
  109. };
  110. }
  111. function installMessageChannelImplementation() {
  112. var channel = new MessageChannel();
  113. channel.port1.onmessage = function(event) {
  114. var handle = event.data;
  115. runIfPresent(handle);
  116. };
  117. registerImmediate = function(handle) {
  118. channel.port2.postMessage(handle);
  119. };
  120. }
  121. function installReadyStateChangeImplementation() {
  122. var html = doc.documentElement;
  123. registerImmediate = function(handle) {
  124. // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
  125. // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
  126. var script = doc.createElement("script");
  127. script.onreadystatechange = function () {
  128. runIfPresent(handle);
  129. script.onreadystatechange = null;
  130. html.removeChild(script);
  131. script = null;
  132. };
  133. html.appendChild(script);
  134. };
  135. }
  136. function installSetTimeoutImplementation() {
  137. registerImmediate = function(handle) {
  138. setTimeout(runIfPresent, 0, handle);
  139. };
  140. }
  141. // If supported, we should attach to the prototype of global, since that is where setTimeout et al. live.
  142. var attachTo = Object.getPrototypeOf && Object.getPrototypeOf(global);
  143. attachTo = attachTo && attachTo.setTimeout ? attachTo : global;
  144. // Don't get fooled by e.g. browserify environments.
  145. if ({}.toString.call(global.process) === "[object process]") {
  146. // For Node.js before 0.9
  147. installNextTickImplementation();
  148. } else if (canUsePostMessage()) {
  149. // For non-IE10 modern browsers
  150. installPostMessageImplementation();
  151. } else if (global.MessageChannel) {
  152. // For web workers, where supported
  153. installMessageChannelImplementation();
  154. } else if (doc && "onreadystatechange" in doc.createElement("script")) {
  155. // For IE 6–8
  156. installReadyStateChangeImplementation();
  157. } else {
  158. // For older browsers
  159. installSetTimeoutImplementation();
  160. }
  161. attachTo.setImmediate = setImmediate;
  162. attachTo.clearImmediate = clearImmediate;
  163. }(typeof self === "undefined" ? typeof global === "undefined" ? this : global : self));