| 1 | # ASAP
|
|---|
| 2 |
|
|---|
| 3 | [](https://travis-ci.org/kriskowal/asap)
|
|---|
| 4 |
|
|---|
| 5 | Promise and asynchronous observer libraries, as well as hand-rolled callback
|
|---|
| 6 | programs and libraries, often need a mechanism to postpone the execution of a
|
|---|
| 7 | callback until the next available event.
|
|---|
| 8 | (See [Designing API’s for Asynchrony][Zalgo].)
|
|---|
| 9 | The `asap` function executes a task **as soon as possible** but not before it
|
|---|
| 10 | returns, waiting only for the completion of the current event and previously
|
|---|
| 11 | scheduled tasks.
|
|---|
| 12 |
|
|---|
| 13 | ```javascript
|
|---|
| 14 | asap(function () {
|
|---|
| 15 | // ...
|
|---|
| 16 | });
|
|---|
| 17 | ```
|
|---|
| 18 |
|
|---|
| 19 | [Zalgo]: http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony
|
|---|
| 20 |
|
|---|
| 21 | This CommonJS package provides an `asap` module that exports a function that
|
|---|
| 22 | executes a task function *as soon as possible*.
|
|---|
| 23 |
|
|---|
| 24 | ASAP strives to schedule events to occur before yielding for IO, reflow,
|
|---|
| 25 | or redrawing.
|
|---|
| 26 | Each event receives an independent stack, with only platform code in parent
|
|---|
| 27 | frames and the events run in the order they are scheduled.
|
|---|
| 28 |
|
|---|
| 29 | ASAP provides a fast event queue that will execute tasks until it is
|
|---|
| 30 | empty before yielding to the JavaScript engine's underlying event-loop.
|
|---|
| 31 | When a task gets added to a previously empty event queue, ASAP schedules a flush
|
|---|
| 32 | event, preferring for that event to occur before the JavaScript engine has an
|
|---|
| 33 | opportunity to perform IO tasks or rendering, thus making the first task and
|
|---|
| 34 | subsequent tasks semantically indistinguishable.
|
|---|
| 35 | ASAP uses a variety of techniques to preserve this invariant on different
|
|---|
| 36 | versions of browsers and Node.js.
|
|---|
| 37 |
|
|---|
| 38 | By design, ASAP prevents input events from being handled until the task
|
|---|
| 39 | queue is empty.
|
|---|
| 40 | If the process is busy enough, this may cause incoming connection requests to be
|
|---|
| 41 | dropped, and may cause existing connections to inform the sender to reduce the
|
|---|
| 42 | transmission rate or stall.
|
|---|
| 43 | ASAP allows this on the theory that, if there is enough work to do, there is no
|
|---|
| 44 | sense in looking for trouble.
|
|---|
| 45 | As a consequence, ASAP can interfere with smooth animation.
|
|---|
| 46 | If your task should be tied to the rendering loop, consider using
|
|---|
| 47 | `requestAnimationFrame` instead.
|
|---|
| 48 | A long sequence of tasks can also effect the long running script dialog.
|
|---|
| 49 | If this is a problem, you may be able to use ASAP’s cousin `setImmediate` to
|
|---|
| 50 | break long processes into shorter intervals and periodically allow the browser
|
|---|
| 51 | to breathe.
|
|---|
| 52 | `setImmediate` will yield for IO, reflow, and repaint events.
|
|---|
| 53 | It also returns a handler and can be canceled.
|
|---|
| 54 | For a `setImmediate` shim, consider [YuzuJS setImmediate][setImmediate].
|
|---|
| 55 |
|
|---|
| 56 | [setImmediate]: https://github.com/YuzuJS/setImmediate
|
|---|
| 57 |
|
|---|
| 58 | Take care.
|
|---|
| 59 | ASAP can sustain infinite recursive calls without warning.
|
|---|
| 60 | It will not halt from a stack overflow, and it will not consume unbounded
|
|---|
| 61 | memory.
|
|---|
| 62 | This is behaviorally equivalent to an infinite loop.
|
|---|
| 63 | Just as with infinite loops, you can monitor a Node.js process for this behavior
|
|---|
| 64 | with a heart-beat signal.
|
|---|
| 65 | As with infinite loops, a very small amount of caution goes a long way to
|
|---|
| 66 | avoiding problems.
|
|---|
| 67 |
|
|---|
| 68 | ```javascript
|
|---|
| 69 | function loop() {
|
|---|
| 70 | asap(loop);
|
|---|
| 71 | }
|
|---|
| 72 | loop();
|
|---|
| 73 | ```
|
|---|
| 74 |
|
|---|
| 75 | In browsers, if a task throws an exception, it will not interrupt the flushing
|
|---|
| 76 | of high-priority tasks.
|
|---|
| 77 | The exception will be postponed to a later, low-priority event to avoid
|
|---|
| 78 | slow-downs.
|
|---|
| 79 | In Node.js, if a task throws an exception, ASAP will resume flushing only if—and
|
|---|
| 80 | only after—the error is handled by `domain.on("error")` or
|
|---|
| 81 | `process.on("uncaughtException")`.
|
|---|
| 82 |
|
|---|
| 83 | ## Raw ASAP
|
|---|
| 84 |
|
|---|
| 85 | Checking for exceptions comes at a cost.
|
|---|
| 86 | The package also provides an `asap/raw` module that exports the underlying
|
|---|
| 87 | implementation which is faster but stalls if a task throws an exception.
|
|---|
| 88 | This internal version of the ASAP function does not check for errors.
|
|---|
| 89 | If a task does throw an error, it will stall the event queue unless you manually
|
|---|
| 90 | call `rawAsap.requestFlush()` before throwing the error, or any time after.
|
|---|
| 91 |
|
|---|
| 92 | In Node.js, `asap/raw` also runs all tasks outside any domain.
|
|---|
| 93 | If you need a task to be bound to your domain, you will have to do it manually.
|
|---|
| 94 |
|
|---|
| 95 | ```js
|
|---|
| 96 | if (process.domain) {
|
|---|
| 97 | task = process.domain.bind(task);
|
|---|
| 98 | }
|
|---|
| 99 | rawAsap(task);
|
|---|
| 100 | ```
|
|---|
| 101 |
|
|---|
| 102 | ## Tasks
|
|---|
| 103 |
|
|---|
| 104 | A task may be any object that implements `call()`.
|
|---|
| 105 | A function will suffice, but closures tend not to be reusable and can cause
|
|---|
| 106 | garbage collector churn.
|
|---|
| 107 | Both `asap` and `rawAsap` accept task objects to give you the option of
|
|---|
| 108 | recycling task objects or using higher callable object abstractions.
|
|---|
| 109 | See the `asap` source for an illustration.
|
|---|
| 110 |
|
|---|
| 111 |
|
|---|
| 112 | ## Compatibility
|
|---|
| 113 |
|
|---|
| 114 | ASAP is tested on Node.js v0.10 and in a broad spectrum of web browsers.
|
|---|
| 115 | The following charts capture the browser test results for the most recent
|
|---|
| 116 | release.
|
|---|
| 117 | The first chart shows test results for ASAP running in the main window context.
|
|---|
| 118 | The second chart shows test results for ASAP running in a web worker context.
|
|---|
| 119 | Test results are inconclusive (grey) on browsers that do not support web
|
|---|
| 120 | workers.
|
|---|
| 121 | These data are captured automatically by [Continuous
|
|---|
| 122 | Integration][].
|
|---|
| 123 |
|
|---|
| 124 | [Continuous Integration]: https://github.com/kriskowal/asap/blob/master/CONTRIBUTING.md
|
|---|
| 125 |
|
|---|
| 126 | 
|
|---|
| 127 |
|
|---|
| 128 | 
|
|---|
| 129 |
|
|---|
| 130 | ## Caveats
|
|---|
| 131 |
|
|---|
| 132 | When a task is added to an empty event queue, it is not always possible to
|
|---|
| 133 | guarantee that the task queue will begin flushing immediately after the current
|
|---|
| 134 | event.
|
|---|
| 135 | However, once the task queue begins flushing, it will not yield until the queue
|
|---|
| 136 | is empty, even if the queue grows while executing tasks.
|
|---|
| 137 |
|
|---|
| 138 | The following browsers allow the use of [DOM mutation observers][] to access
|
|---|
| 139 | the HTML [microtask queue][], and thus begin flushing ASAP's task queue
|
|---|
| 140 | immediately at the end of the current event loop turn, before any rendering or
|
|---|
| 141 | IO:
|
|---|
| 142 |
|
|---|
| 143 | [microtask queue]: http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#microtask-queue
|
|---|
| 144 | [DOM mutation observers]: http://dom.spec.whatwg.org/#mutation-observers
|
|---|
| 145 |
|
|---|
| 146 | - Android 4–4.3
|
|---|
| 147 | - Chrome 26–34
|
|---|
| 148 | - Firefox 14–29
|
|---|
| 149 | - Internet Explorer 11
|
|---|
| 150 | - iPad Safari 6–7.1
|
|---|
| 151 | - iPhone Safari 7–7.1
|
|---|
| 152 | - Safari 6–7
|
|---|
| 153 |
|
|---|
| 154 | In the absense of mutation observers, there are a few browsers, and situations
|
|---|
| 155 | like web workers in some of the above browsers, where [message channels][]
|
|---|
| 156 | would be a useful way to avoid falling back to timers.
|
|---|
| 157 | Message channels give direct access to the HTML [task queue][], so the ASAP
|
|---|
| 158 | task queue would flush after any already queued rendering and IO tasks, but
|
|---|
| 159 | without having the minimum delay imposed by timers.
|
|---|
| 160 | However, among these browsers, Internet Explorer 10 and Safari do not reliably
|
|---|
| 161 | dispatch messages, so they are not worth the trouble to implement.
|
|---|
| 162 |
|
|---|
| 163 | [message channels]: http://www.whatwg.org/specs/web-apps/current-work/multipage/web-messaging.html#message-channels
|
|---|
| 164 | [task queue]: http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#concept-task
|
|---|
| 165 |
|
|---|
| 166 | - Internet Explorer 10
|
|---|
| 167 | - Safair 5.0-1
|
|---|
| 168 | - Opera 11-12
|
|---|
| 169 |
|
|---|
| 170 | In the absense of mutation observers, these browsers and the following browsers
|
|---|
| 171 | all fall back to using `setTimeout` and `setInterval` to ensure that a `flush`
|
|---|
| 172 | occurs.
|
|---|
| 173 | The implementation uses both and cancels whatever handler loses the race, since
|
|---|
| 174 | `setTimeout` tends to occasionally skip tasks in unisolated circumstances.
|
|---|
| 175 | Timers generally delay the flushing of ASAP's task queue for four milliseconds.
|
|---|
| 176 |
|
|---|
| 177 | - Firefox 3–13
|
|---|
| 178 | - Internet Explorer 6–10
|
|---|
| 179 | - iPad Safari 4.3
|
|---|
| 180 | - Lynx 2.8.7
|
|---|
| 181 |
|
|---|
| 182 |
|
|---|
| 183 | ## Heritage
|
|---|
| 184 |
|
|---|
| 185 | ASAP has been factored out of the [Q][] asynchronous promise library.
|
|---|
| 186 | It originally had a naïve implementation in terms of `setTimeout`, but
|
|---|
| 187 | [Malte Ubl][NonBlocking] provided an insight that `postMessage` might be
|
|---|
| 188 | useful for creating a high-priority, no-delay event dispatch hack.
|
|---|
| 189 | Since then, Internet Explorer proposed and implemented `setImmediate`.
|
|---|
| 190 | Robert Katić began contributing to Q by measuring the performance of
|
|---|
| 191 | the internal implementation of `asap`, paying particular attention to
|
|---|
| 192 | error recovery.
|
|---|
| 193 | Domenic, Robert, and Kris Kowal collectively settled on the current strategy of
|
|---|
| 194 | unrolling the high-priority event queue internally regardless of what strategy
|
|---|
| 195 | we used to dispatch the potentially lower-priority flush event.
|
|---|
| 196 | Domenic went on to make ASAP cooperate with Node.js domains.
|
|---|
| 197 |
|
|---|
| 198 | [Q]: https://github.com/kriskowal/q
|
|---|
| 199 | [NonBlocking]: http://www.nonblocking.io/2011/06/windownexttick.html
|
|---|
| 200 |
|
|---|
| 201 | For further reading, Nicholas Zakas provided a thorough article on [The
|
|---|
| 202 | Case for setImmediate][NCZ].
|
|---|
| 203 |
|
|---|
| 204 | [NCZ]: http://www.nczonline.net/blog/2013/07/09/the-case-for-setimmediate/
|
|---|
| 205 |
|
|---|
| 206 | Ember’s RSVP promise implementation later [adopted][RSVP ASAP] the name ASAP but
|
|---|
| 207 | further developed the implentation.
|
|---|
| 208 | Particularly, The `MessagePort` implementation was abandoned due to interaction
|
|---|
| 209 | [problems with Mobile Internet Explorer][IE Problems] in favor of an
|
|---|
| 210 | implementation backed on the newer and more reliable DOM `MutationObserver`
|
|---|
| 211 | interface.
|
|---|
| 212 | These changes were back-ported into this library.
|
|---|
| 213 |
|
|---|
| 214 | [IE Problems]: https://github.com/cujojs/when/issues/197
|
|---|
| 215 | [RSVP ASAP]: https://github.com/tildeio/rsvp.js/blob/cddf7232546a9cf858524b75cde6f9edf72620a7/lib/rsvp/asap.js
|
|---|
| 216 |
|
|---|
| 217 | In addition, ASAP factored into `asap` and `asap/raw`, such that `asap` remained
|
|---|
| 218 | exception-safe, but `asap/raw` provided a tight kernel that could be used for
|
|---|
| 219 | tasks that guaranteed that they would not throw exceptions.
|
|---|
| 220 | This core is useful for promise implementations that capture thrown errors in
|
|---|
| 221 | rejected promises and do not need a second safety net.
|
|---|
| 222 | At the same time, the exception handling in `asap` was factored into separate
|
|---|
| 223 | implementations for Node.js and browsers, using the the [Browserify][Browser
|
|---|
| 224 | Config] `browser` property in `package.json` to instruct browser module loaders
|
|---|
| 225 | and bundlers, including [Browserify][], [Mr][], and [Mop][], to use the
|
|---|
| 226 | browser-only implementation.
|
|---|
| 227 |
|
|---|
| 228 | [Browser Config]: https://gist.github.com/defunctzombie/4339901
|
|---|
| 229 | [Browserify]: https://github.com/substack/node-browserify
|
|---|
| 230 | [Mr]: https://github.com/montagejs/mr
|
|---|
| 231 | [Mop]: https://github.com/montagejs/mop
|
|---|
| 232 |
|
|---|
| 233 | ## License
|
|---|
| 234 |
|
|---|
| 235 | Copyright 2009-2014 by Contributors
|
|---|
| 236 | MIT License (enclosed)
|
|---|
| 237 |
|
|---|