1 Asynchronous Operations
2 =======================
4 As a language (and runtime), javascript is fundamentally
5 single-threaded. This means any blocking request or computation will
6 blocks the whole page (and, in older browsers, the software itself
7 even preventing users from switching to an other tab): a javascript
8 environment can be seen as an event-based runloop where application
9 developers have no control over the runloop itself.
11 As a result, performing long-running synchronous network requests or
12 other types of complex and expensive accesses is frowned upon and
13 asynchronous APIs are used instead.
15 Asynchronous code rarely comes naturally, especially for developers
16 used to synchronous server-side code (in Python, Java or C#) where the
17 code will just block until the deed is gone. This is increased further
18 when asynchronous programming is not a first-class concept and is
19 instead implemented on top of callbacks-based programming, which is
20 the case in javascript.
22 The goal of this guide is to provide some tools to deal with
23 asynchronous systems, and warn against systematic issues or dangers.
28 Deferreds are a form of `promises`_. OpenERP Web currently uses
31 The core idea of deferreds is that potentially asynchronous methods
32 will return a :js:class:`Deferred` object instead of an arbitrary
33 value or (most commonly) nothing.
35 This object can then be used to track the end of the asynchronous
36 operation by adding callbacks onto it, either success callbacks or
39 A great advantage of deferreds over simply passing callback functions
40 directly to asynchronous methods is the ability to :ref:`compose them
41 <deferred-composition>`.
46 Deferreds's most important method is :js:func:`Deferred.then`. It is
47 used to attach new callbacks to the deferred object.
49 * the first parameter attaches a success callback, called when the
50 deferred object is successfully resolved and provided with the
51 resolved value(s) for the asynchronous operation.
53 * the second parameter attaches a failure callback, called when the
54 deferred object is rejected and provided with rejection values
55 (often some sort of error message).
57 Callbacks attached to deferreds are never "lost": if a callback is
58 attached to an already resolved or rejected deferred, the callback
59 will be called (or ignored) immediately. A deferred is also only ever
60 resolved or rejected once, and is either resolved or rejected: a given
61 deferred can not call a single success callback twice, or call both a
62 success and a failure callbacks.
64 :js:func:`~Deferred.then` should be the method you'll use most often
65 when interacting with deferred objects (and thus asynchronous APIs).
70 After using asynchronous APIs may come the time to build them: for
71 `mocks`_, to compose deferreds from multiple source in a complex
72 manner, in order to let the current operations repaint the screen or
73 give other events the time to unfold, ...
75 This is easy using jQuery's deferred objects.
77 .. note:: this section is an implementation detail of jQuery Deferred
78 objects, the creation of promises is not part of any
79 standard (even tentative) that I know of. If you are using
80 deferred objects which are not jQuery's, their API may (and
81 often will) be completely different.
83 Deferreds are created by invoking their constructor [#]_ without any
84 argument. This creates a :js:class:`Deferred` instance object with the
87 :js:func:`Deferred.resolve`
89 As its name indicates, this method moves the deferred to the
90 "Resolved" state. It can be provided as many arguments as
91 necessary, these arguments will be provided to any pending success
94 :js:func:`Deferred.reject`
96 Similar to :js:func:`~Deferred.resolve`, but moves the deferred to
97 the "Rejected" state and calls pending failure handlers.
99 :js:func:`Deferred.promise`
101 Creates a readonly view of the deferred object. It is generally a
102 good idea to return a promise view of the deferred to prevent
103 callers from resolving or rejecting the deferred in your stead.
105 :js:func:`~Deferred.reject` and :js:func:`~Deferred.resolve` are used
106 to inform callers that the asynchronous operation has failed (or
107 succeeded). These methods should simply be called when the
108 asynchronous operation has ended, to notify anybody interested in its
111 .. _deferred-composition:
116 What we've seen so far is pretty nice, but mostly doable by passing
117 functions to other functions (well adding functions post-facto would
118 probably be a chore... still, doable).
120 Deferreds truly shine when code needs to compose asynchronous
121 operations in some way or other, as they can be used as a basis for
124 There are two main forms of compositions over deferred: multiplexing
125 and piping/cascading.
127 Deferred multiplexing
128 `````````````````````
130 The most common reason for multiplexing deferred is simply performing
131 2+ asynchronous operations and wanting to wait until all of them are
132 done before moving on (and executing more stuff).
134 The jQuery multiplexing function for promises is :js:func:`when`.
136 .. note:: the multiplexing behavior of jQuery's :js:func:`when` is an
137 (incompatible, mostly) extension of the behavior defined in
138 `CommonJS Promises/B`_.
140 This function can take any number of promises [#]_ and will return a
143 This returned promise will be resolved when *all* multiplexed promises
144 are resolved, and will be rejected as soon as one of the multiplexed
145 promises is rejected (it behaves like Python's ``all()``, but with
146 promise objects instead of boolean-ish).
148 The resolved values of the various promises multiplexed via
149 :js:func:`when` are mapped to the arguments of :js:func:`when`'s
150 success callback, if they are needed. The resolved values of a promise
151 are at the same index in the callback's arguments as the promise in
152 the :js:func:`when` call so you will have:
154 .. code-block:: javascript
156 $.when(p0, p1, p2, p3).then(
157 function (results0, results1, results2, results3) {
163 in a normal mapping, each parameter to the callback would be an
164 array: each promise is conceptually resolved with an array of 0..n
165 values and these values are passed to :js:func:`when`'s
166 callback. But jQuery treats deferreds resolving a single value
167 specially, and "unwraps" that value.
169 For instance, in the code block above if the index of each promise
170 is the number of values it resolves (0 to 3), ``results0`` is an
171 empty array, ``results2`` is an array of 2 elements (a pair) but
172 ``results1`` is the actual value resolved by ``p1``, not an array.
177 A second useful composition is starting an asynchronous operation as
178 the result of an other asynchronous operation, and wanting the result
179 of both: with the tools described so far, handling e.g. OpenERP's
180 search/read sequence with this would require something along the lines
183 .. code-block:: javascript
185 var result = $.Deferred();
186 Model.search(condition).then(function (ids) {
187 Model.read(ids, fields).then(function (records) {
188 result.resolve(records);
191 return result.promise();
193 While it doesn't look too bad for trivial code, this quickly gets
196 But :js:func:`~Deferred.then` also allows handling this kind of
197 chains: it returns a new promise object, not the one it was called
198 with, and the return values of the callbacks is actually important to
199 it: whichever callback is called,
201 * If the callback is not set (not provided or left to null), the
202 resolution or rejection value(s) is simply forwarded to
203 :js:func:`~Deferred.then`'s promise (it's essentially a noop)
205 * If the callback is set and does not return an observable object (a
206 deferred or a promise), the value it returns (``undefined`` if it
207 does not return anything) will replace the value it was given, e.g.
209 .. code-block:: javascript
211 promise.then(function () {
212 console.log('called');
215 will resolve with the sole value ``undefined``.
217 * If the callback is set and returns an observable object, that object
218 will be the actual resolution (and result) of the pipe. This means a
219 resolved promise from the failure callback will resolve the pipe,
220 and a failure promise from the success callback will reject the
223 This provides an easy way to chain operation successes, and the
224 previous piece of code can now be rewritten:
226 .. code-block:: javascript
228 return Model.search(condition).then(function (ids) {
229 return Model.read(ids, fields);
232 the result of the whole expression will encode failure if either
233 ``search`` or ``read`` fails (with the right rejection values), and
234 will be resolved with ``read``'s resolution values if the chain
237 :js:func:`~Deferred.then` is also useful to adapt third-party
238 promise-based APIs, in order to filter their resolution value counts
239 for instance (to take advantage of :js:func:`when` 's special
240 treatment of single-value promises).
246 .. js:function:: when(deferreds…)
248 :param deferreds: deferred objects to multiplex
249 :returns: a multiplexed deferred
250 :rtype: :js:class:`Deferred`
252 .. js:class:: Deferred
254 .. js:function:: Deferred.then(doneCallback[, failCallback])
256 Attaches new callbacks to the resolution or rejection of the
257 deferred object. Callbacks are executed in the order they are
258 attached to the deferred.
260 To provide only a failure callback, pass ``null`` as the
261 ``doneCallback``, to provide only a success callback the
262 second argument can just be ignored (and not passed at all).
264 Returns a new deferred which resolves to the result of the
265 corresponding callback, if a callback returns a deferred
266 itself that new deferred will be used as the resolution of the
269 :param doneCallback: function called when the deferred is resolved
270 :type doneCallback: Function
271 :param failCallback: function called when the deferred is rejected
272 :type failCallback: Function
273 :returns: the deferred object on which it was called
274 :rtype: :js:class:`Deferred`
276 .. js:function:: Deferred.done(doneCallback)
278 Attaches a new success callback to the deferred, shortcut for
279 ``deferred.then(doneCallback)``.
281 This is a jQuery extension to `CommonJS Promises/A`_ providing
282 little value over calling :js:func:`~Deferred.then` directly,
283 it should be avoided.
285 :param doneCallback: function called when the deferred is resolved
286 :type doneCallback: Function
287 :returns: the deferred object on which it was called
288 :rtype: :js:class:`Deferred`
290 .. js:function:: Deferred.fail(failCallback)
292 Attaches a new failure callback to the deferred, shortcut for
293 ``deferred.then(null, failCallback)``.
295 A second jQuery extension to `Promises/A <CommonJS
296 Promises/A>`_. Although it provides more value than
297 :js:func:`~Deferred.done`, it still is not much and should be
300 :param failCallback: function called when the deferred is rejected
301 :type failCallback: Function
302 :returns: the deferred object on which it was called
303 :rtype: :js:class:`Deferred`
305 .. js:function:: Deferred.promise()
307 Returns a read-only view of the deferred object, with all
308 mutators (resolve and reject) methods removed.
310 .. js:function:: Deferred.resolve(value…)
312 Called to resolve a deferred, any value provided will be
313 passed onto the success handlers of the deferred object.
315 Resolving a deferred which has already been resolved or
316 rejected has no effect.
318 .. js:function:: Deferred.reject(value…)
320 Called to reject (fail) a deferred, any value provided will be
321 passed onto the failure handler of the deferred object.
323 Rejecting a deferred which has already been resolved or
324 rejected has no effect.
326 .. [#] or simply calling :js:class:`Deferred` as a function, the
329 .. [#] or not-promises, the `CommonJS Promises/B`_ role of
330 :js:func:`when` is to be able to treat values and promises
331 uniformly: :js:func:`when` will pass promises through directly,
332 but non-promise values and objects will be transformed into a
333 resolved promise (resolving themselves with the value itself).
335 jQuery's :js:func:`when` keeps this behavior making deferreds
336 easy to build from "static" values, or allowing defensive code
337 where expected promises are wrapped in :js:func:`when` just in
340 .. _promises: http://en.wikipedia.org/wiki/Promise_(programming)
341 .. _jQuery's deferred: http://api.jquery.com/category/deferred-object/
342 .. _CommonJS Promises/A: http://wiki.commonjs.org/wiki/Promises/A
343 .. _CommonJS Promises/B: http://wiki.commonjs.org/wiki/Promises/B
344 .. _mocks: http://en.wikipedia.org/wiki/Mock_object