setImmediate.js (6475B)
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));