Immediate.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. /**
  2. Some credit for this helper goes to http://github.com/YuzuJS/setImmediate
  3. */
  4. "use strict";
  5. var root_1 = require('./root');
  6. var ImmediateDefinition = (function () {
  7. function ImmediateDefinition(root) {
  8. this.root = root;
  9. if (root.setImmediate && typeof root.setImmediate === 'function') {
  10. this.setImmediate = root.setImmediate.bind(root);
  11. this.clearImmediate = root.clearImmediate.bind(root);
  12. }
  13. else {
  14. this.nextHandle = 1;
  15. this.tasksByHandle = {};
  16. this.currentlyRunningATask = false;
  17. // Don't get fooled by e.g. browserify environments.
  18. if (this.canUseProcessNextTick()) {
  19. // For Node.js before 0.9
  20. this.setImmediate = this.createProcessNextTickSetImmediate();
  21. }
  22. else if (this.canUsePostMessage()) {
  23. // For non-IE10 modern browsers
  24. this.setImmediate = this.createPostMessageSetImmediate();
  25. }
  26. else if (this.canUseMessageChannel()) {
  27. // For web workers, where supported
  28. this.setImmediate = this.createMessageChannelSetImmediate();
  29. }
  30. else if (this.canUseReadyStateChange()) {
  31. // For IE 6–8
  32. this.setImmediate = this.createReadyStateChangeSetImmediate();
  33. }
  34. else {
  35. // For older browsers
  36. this.setImmediate = this.createSetTimeoutSetImmediate();
  37. }
  38. var ci = function clearImmediate(handle) {
  39. delete clearImmediate.instance.tasksByHandle[handle];
  40. };
  41. ci.instance = this;
  42. this.clearImmediate = ci;
  43. }
  44. }
  45. ImmediateDefinition.prototype.identify = function (o) {
  46. return this.root.Object.prototype.toString.call(o);
  47. };
  48. ImmediateDefinition.prototype.canUseProcessNextTick = function () {
  49. return this.identify(this.root.process) === '[object process]';
  50. };
  51. ImmediateDefinition.prototype.canUseMessageChannel = function () {
  52. return Boolean(this.root.MessageChannel);
  53. };
  54. ImmediateDefinition.prototype.canUseReadyStateChange = function () {
  55. var document = this.root.document;
  56. return Boolean(document && 'onreadystatechange' in document.createElement('script'));
  57. };
  58. ImmediateDefinition.prototype.canUsePostMessage = function () {
  59. var root = this.root;
  60. // The test against `importScripts` prevents this implementation from being installed inside a web worker,
  61. // where `root.postMessage` means something completely different and can't be used for this purpose.
  62. if (root.postMessage && !root.importScripts) {
  63. var postMessageIsAsynchronous_1 = true;
  64. var oldOnMessage = root.onmessage;
  65. root.onmessage = function () {
  66. postMessageIsAsynchronous_1 = false;
  67. };
  68. root.postMessage('', '*');
  69. root.onmessage = oldOnMessage;
  70. return postMessageIsAsynchronous_1;
  71. }
  72. return false;
  73. };
  74. // This function accepts the same arguments as setImmediate, but
  75. // returns a function that requires no arguments.
  76. ImmediateDefinition.prototype.partiallyApplied = function (handler) {
  77. var args = [];
  78. for (var _i = 1; _i < arguments.length; _i++) {
  79. args[_i - 1] = arguments[_i];
  80. }
  81. var fn = function result() {
  82. var _a = result, handler = _a.handler, args = _a.args;
  83. if (typeof handler === 'function') {
  84. handler.apply(undefined, args);
  85. }
  86. else {
  87. (new Function('' + handler))();
  88. }
  89. };
  90. fn.handler = handler;
  91. fn.args = args;
  92. return fn;
  93. };
  94. ImmediateDefinition.prototype.addFromSetImmediateArguments = function (args) {
  95. this.tasksByHandle[this.nextHandle] = this.partiallyApplied.apply(undefined, args);
  96. return this.nextHandle++;
  97. };
  98. ImmediateDefinition.prototype.createProcessNextTickSetImmediate = function () {
  99. var fn = function setImmediate() {
  100. var instance = setImmediate.instance;
  101. var handle = instance.addFromSetImmediateArguments(arguments);
  102. instance.root.process.nextTick(instance.partiallyApplied(instance.runIfPresent, handle));
  103. return handle;
  104. };
  105. fn.instance = this;
  106. return fn;
  107. };
  108. ImmediateDefinition.prototype.createPostMessageSetImmediate = function () {
  109. // Installs an event handler on `global` for the `message` event: see
  110. // * https://developer.mozilla.org/en/DOM/window.postMessage
  111. // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages
  112. var root = this.root;
  113. var messagePrefix = 'setImmediate$' + root.Math.random() + '$';
  114. var onGlobalMessage = function globalMessageHandler(event) {
  115. var instance = globalMessageHandler.instance;
  116. if (event.source === root &&
  117. typeof event.data === 'string' &&
  118. event.data.indexOf(messagePrefix) === 0) {
  119. instance.runIfPresent(+event.data.slice(messagePrefix.length));
  120. }
  121. };
  122. onGlobalMessage.instance = this;
  123. root.addEventListener('message', onGlobalMessage, false);
  124. var fn = function setImmediate() {
  125. var _a = setImmediate, messagePrefix = _a.messagePrefix, instance = _a.instance;
  126. var handle = instance.addFromSetImmediateArguments(arguments);
  127. instance.root.postMessage(messagePrefix + handle, '*');
  128. return handle;
  129. };
  130. fn.instance = this;
  131. fn.messagePrefix = messagePrefix;
  132. return fn;
  133. };
  134. ImmediateDefinition.prototype.runIfPresent = function (handle) {
  135. // From the spec: 'Wait until any invocations of this algorithm started before this one have completed.'
  136. // So if we're currently running a task, we'll need to delay this invocation.
  137. if (this.currentlyRunningATask) {
  138. // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a
  139. // 'too much recursion' error.
  140. this.root.setTimeout(this.partiallyApplied(this.runIfPresent, handle), 0);
  141. }
  142. else {
  143. var task = this.tasksByHandle[handle];
  144. if (task) {
  145. this.currentlyRunningATask = true;
  146. try {
  147. task();
  148. }
  149. finally {
  150. this.clearImmediate(handle);
  151. this.currentlyRunningATask = false;
  152. }
  153. }
  154. }
  155. };
  156. ImmediateDefinition.prototype.createMessageChannelSetImmediate = function () {
  157. var _this = this;
  158. var channel = new this.root.MessageChannel();
  159. channel.port1.onmessage = function (event) {
  160. var handle = event.data;
  161. _this.runIfPresent(handle);
  162. };
  163. var fn = function setImmediate() {
  164. var _a = setImmediate, channel = _a.channel, instance = _a.instance;
  165. var handle = instance.addFromSetImmediateArguments(arguments);
  166. channel.port2.postMessage(handle);
  167. return handle;
  168. };
  169. fn.channel = channel;
  170. fn.instance = this;
  171. return fn;
  172. };
  173. ImmediateDefinition.prototype.createReadyStateChangeSetImmediate = function () {
  174. var fn = function setImmediate() {
  175. var instance = setImmediate.instance;
  176. var root = instance.root;
  177. var doc = root.document;
  178. var html = doc.documentElement;
  179. var handle = instance.addFromSetImmediateArguments(arguments);
  180. // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
  181. // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
  182. var script = doc.createElement('script');
  183. script.onreadystatechange = function () {
  184. instance.runIfPresent(handle);
  185. script.onreadystatechange = null;
  186. html.removeChild(script);
  187. script = null;
  188. };
  189. html.appendChild(script);
  190. return handle;
  191. };
  192. fn.instance = this;
  193. return fn;
  194. };
  195. ImmediateDefinition.prototype.createSetTimeoutSetImmediate = function () {
  196. var fn = function setImmediate() {
  197. var instance = setImmediate.instance;
  198. var handle = instance.addFromSetImmediateArguments(arguments);
  199. instance.root.setTimeout(instance.partiallyApplied(instance.runIfPresent, handle), 0);
  200. return handle;
  201. };
  202. fn.instance = this;
  203. return fn;
  204. };
  205. return ImmediateDefinition;
  206. }());
  207. exports.ImmediateDefinition = ImmediateDefinition;
  208. exports.Immediate = new ImmediateDefinition(root_1.root);
  209. //# sourceMappingURL=Immediate.js.map