UNPKG

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