UNPKG

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