3 // Use the fastest means possible to execute a task in its own turn, with
4 // priority over other events including IO, animation, reflow, and redraw
7 // An exception thrown by a task will permanently interrupt the processing of
8 // subsequent tasks. The higher level `asap` function ensures that if an
9 // exception is thrown by a task, that the task queue will continue flushing as
10 // soon as possible, but if you use `rawAsap` directly, you are responsible to
11 // either ensure that no exceptions are thrown from your task, or to manually
12 // call `rawAsap.requestFlush` if an exception is thrown.
13 module.exports = rawAsap;
14 function rawAsap(task) {
19 // Equivalent to push, but avoids a function call.
20 queue[queue.length] = task;
24 // Once a flush has been requested, no further calls to `requestFlush` are
25 // necessary until the next `flush` completes.
27 // `requestFlush` is an implementation-specific method that attempts to kick
28 // off a `flush` event as quickly as possible. `flush` will attempt to exhaust
29 // the event queue before yielding to the browser's own event loop.
31 // The position of the next task to execute in the task queue. This is
32 // preserved between calls to `flush` so that it can be resumed if
33 // a task throws an exception.
35 // If a task schedules additional tasks recursively, the task queue can grow
36 // unbounded. To prevent memory exhaustion, the task queue will periodically
37 // truncate already-completed tasks.
40 // The flush function processes all tasks that have been scheduled with
41 // `rawAsap` unless and until one of those tasks throws an exception.
42 // If a task throws an exception, `flush` ensures that its state will remain
43 // consistent and will resume where it left off when called again.
44 // However, `flush` does not make any arrangements to be called again if an
45 // exception is thrown.
47 while (index < queue.length) {
48 var currentIndex = index;
49 // Advance the index before calling the task. This ensures that we will
50 // begin flushing on the next task the task throws an error.
52 queue[currentIndex].call();
53 // Prevent leaking memory for long chains of recursive calls to `asap`.
54 // If we call `asap` within tasks scheduled by `asap`, the queue will
55 // grow, but to avoid an O(n) walk for every task we execute, we don't
56 // shift tasks off the queue after they have been executed.
57 // Instead, we periodically shift 1024 tasks off the queue.
58 if (index > capacity) {
59 // Manually shift all values starting at the index back to the
60 // beginning of the queue.
61 for (var scan = 0, newLength = queue.length - index; scan < newLength; scan++) {
62 queue[scan] = queue[scan + index];
64 queue.length -= index;
73 // `requestFlush` is implemented using a strategy based on data collected from
74 // every available SauceLabs Selenium web driver worker at time of writing.
75 // https://docs.google.com/spreadsheets/d/1mG-5UYGup5qxGdEMWkhP6BWCz053NUb2E1QoUTU16uA/edit#gid=783724593
77 // Safari 6 and 6.1 for desktop, iPad, and iPhone are the only browsers that
78 // have WebKitMutationObserver but not un-prefixed MutationObserver.
79 // Must use `global` instead of `window` to work in both frames and web
80 // workers. `global` is a provision of Browserify, Mr, Mrs, or Mop.
81 var BrowserMutationObserver = global.MutationObserver || global.WebKitMutationObserver;
83 // MutationObservers are desirable because they have high priority and work
84 // reliably everywhere they are implemented.
85 // They are implemented in all modern browsers.
90 // - Internet Explorer 11
91 // - iPad Safari 6-7.1
92 // - iPhone Safari 7-7.1
94 if (typeof BrowserMutationObserver === "function") {
95 requestFlush = makeRequestCallFromMutationObserver(flush);
97 // MessageChannels are desirable because they give direct access to the HTML
98 // task queue, are implemented in Internet Explorer 10, Safari 5.0-1, and Opera
99 // 11-12, and in web workers in many engines.
100 // Although message channels yield to any queued rendering and IO tasks, they
101 // would be better than imposing the 4ms delay of timers.
102 // However, they do not work reliably in Internet Explorer or Safari.
104 // Internet Explorer 10 is the only browser that has setImmediate but does
105 // not have MutationObservers.
106 // Although setImmediate yields to the browser's renderer, it would be
107 // preferrable to falling back to setTimeout since it does not have
108 // the minimum 4ms penalty.
109 // Unfortunately there appears to be a bug in Internet Explorer 10 Mobile (and
110 // Desktop to a lesser extent) that renders both setImmediate and
111 // MessageChannel useless for the purposes of ASAP.
112 // https://github.com/kriskowal/q/issues/396
114 // Timers are implemented universally.
115 // We fall back to timers in workers in most engines, and in foreground
116 // contexts in the following browsers.
117 // However, note that even this simple case requires nuances to operate in a
118 // broad spectrum of browsers.
121 // - Internet Explorer 6-9
125 requestFlush = makeRequestCallFromTimer(flush);
128 // `requestFlush` requests that the high priority event queue be flushed as
130 // This is useful to prevent an error thrown in a task from stalling the event
131 // queue if the exception handled by Node.js’s
132 // `process.on("uncaughtException")` or by a domain.
133 rawAsap.requestFlush = requestFlush;
135 // To request a high priority event, we induce a mutation observer by toggling
136 // the text of a text node between "1" and "-1".
137 function makeRequestCallFromMutationObserver(callback) {
139 var observer = new BrowserMutationObserver(callback);
140 var node = document.createTextNode("");
141 observer.observe(node, {characterData: true});
142 return function requestCall() {
148 // The message channel technique was discovered by Malte Ubl and was the
149 // original foundation for this library.
150 // http://www.nonblocking.io/2011/06/windownexttick.html
152 // Safari 6.0.5 (at least) intermittently fails to create message ports on a
153 // page's first load. Thankfully, this version of Safari supports
154 // MutationObservers, so we don't need to fall back in that case.
156 // function makeRequestCallFromMessageChannel(callback) {
157 // var channel = new MessageChannel();
158 // channel.port1.onmessage = callback;
159 // return function requestCall() {
160 // channel.port2.postMessage(0);
164 // For reasons explained above, we are also unable to use `setImmediate`
165 // under any circumstances.
166 // Even if we were, there is another bug in Internet Explorer 10.
167 // It is not sufficient to assign `setImmediate` to `requestFlush` because
168 // `setImmediate` must be called *by name* and therefore must be wrapped in a
172 // function makeRequestCallFromSetImmediate(callback) {
173 // return function requestCall() {
174 // setImmediate(callback);
178 // Safari 6.0 has a problem where timers will get lost while the user is
179 // scrolling. This problem does not impact ASAP because Safari 6.0 supports
180 // mutation observers, so that implementation is used instead.
181 // However, if we ever elect to use timers in Safari, the prevalent work-around
182 // is to add a scroll event listener that calls for a flush.
184 // `setTimeout` does not call the passed callback if the delay is less than
185 // approximately 7 in web workers in Firefox 8 through 18, and sometimes not
188 function makeRequestCallFromTimer(callback) {
189 return function requestCall() {
190 // We dispatch a timeout with a specified delay of 0 for engines that
191 // can reliably accommodate that request. This will usually be snapped
192 // to a 4 milisecond delay, but once we're flushing, there's no delay
194 var timeoutHandle = setTimeout(handleTimer, 0);
195 // However, since this timer gets frequently dropped in Firefox
196 // workers, we enlist an interval handle that will try to fire
197 // an event 20 times per second until it succeeds.
198 var intervalHandle = setInterval(handleTimer, 50);
200 function handleTimer() {
201 // Whichever timer succeeds will cancel both timers and
202 // execute the callback.
203 clearTimeout(timeoutHandle);
204 clearInterval(intervalHandle);
210 // This is for `asap.js` only.
211 // Its name will be periodically randomized to break any code that depends on
213 rawAsap.makeRequestCallFromTimer = makeRequestCallFromTimer;
215 // ASAP was originally a nextTick shim included in Q. This was factored out
216 // into this ASAP package. It was later adapted to RSVP which made further
217 // amendments. These decisions, particularly to marginalize MessageChannel and
218 // to capture the MutationObserver implementation in a closure, were integrated
219 // back into ASAP proper.
220 // https://github.com/tildeio/rsvp.js/blob/cddf7232546a9cf858524b75cde6f9edf72620a7/lib/rsvp/asap.js