[FIX] Set Login params.token. Added action_notify and action_warn
[odoo/odoo.git] / addons / web / doc / async.rst
1 Asynchronous Operations
2 =======================
3
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.
10
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.
14
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.
21
22 The goal of this guide is to provide some tools to deal with
23 asynchronous systems, and warn against systematic issues or dangers.
24
25 Deferreds
26 ---------
27
28 Deferreds are a form of `promises`_. OpenERP Web currently uses
29 `jQuery's deferred`_.
30
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.
34
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
37 error callbacks.
38
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>`.
42
43 Using deferreds
44 ~~~~~~~~~~~~~~~
45
46 Deferreds's most important method is :js:func:`Deferred.then`. It is
47 used to attach new callbacks to the deferred object.
48
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.
52
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).
56
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.
63
64 :js:func:`~Deferred.then` should be the method you'll use most often
65 when interacting with deferred objects (and thus asynchronous APIs).
66
67 Building deferreds
68 ~~~~~~~~~~~~~~~~~~
69
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, ...
74
75 This is easy using jQuery's deferred objects.
76
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.
82
83 Deferreds are created by invoking their constructor [#]_ without any
84 argument. This creates a :js:class:`Deferred` instance object with the
85 following methods:
86
87 :js:func:`Deferred.resolve`
88
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
92     callback.
93
94 :js:func:`Deferred.reject`
95
96     Similar to :js:func:`~Deferred.resolve`, but moves the deferred to
97     the "Rejected" state and calls pending failure handlers.
98
99 :js:func:`Deferred.promise`
100
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.
104
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
109 result(s).
110
111 .. _deferred-composition:
112
113 Composing deferreds
114 ~~~~~~~~~~~~~~~~~~~
115
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).
119
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
122 such composition.
123
124 There are two main forms of compositions over deferred: multiplexing
125 and piping/cascading.
126
127 Deferred multiplexing
128 `````````````````````
129
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).
133
134 The jQuery multiplexing function for promises is :js:func:`when`.
135
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`_.
139
140 This function can take any number of promises [#]_ and will return a
141 promise.
142
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).
147
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:
153
154 .. code-block:: javascript
155
156     $.when(p0, p1, p2, p3).then(
157             function (results0, results1, results2, results3) {
158         // code
159     });
160
161 .. warning::
162
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.
168
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.
173
174 Deferred chaining
175 `````````````````
176
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
181 of:
182
183 .. code-block:: javascript
184
185     var result = $.Deferred();
186     Model.search(condition).then(function (ids) {
187         Model.read(ids, fields).then(function (records) {
188             result.resolve(records);
189         });
190     });
191     return result.promise();
192
193 While it doesn't look too bad for trivial code, this quickly gets
194 unwieldy.
195
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,
200
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)
204
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.
208
209   .. code-block:: javascript
210
211       promise.then(function () {
212           console.log('called');
213       });
214
215   will resolve with the sole value ``undefined``.
216
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
221   pipe.
222
223   This provides an easy way to chain operation successes, and the
224   previous piece of code can now be rewritten:
225
226   .. code-block:: javascript
227
228       return Model.search(condition).then(function (ids) {
229           return Model.read(ids, fields);
230       });
231
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
235   executes correctly.
236
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).
241
242
243 jQuery.Deferred API
244 ~~~~~~~~~~~~~~~~~~~
245
246 .. js:function:: when(deferreds…)
247
248     :param deferreds: deferred objects to multiplex
249     :returns: a multiplexed deferred
250     :rtype: :js:class:`Deferred`
251
252 .. js:class:: Deferred
253
254     .. js:function:: Deferred.then(doneCallback[, failCallback])
255
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.
259
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).
263
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
267         chain.
268
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`
275
276     .. js:function:: Deferred.done(doneCallback)
277
278         Attaches a new success callback to the deferred, shortcut for
279         ``deferred.then(doneCallback)``.
280
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.
284
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`
289
290     .. js:function:: Deferred.fail(failCallback)
291
292         Attaches a new failure callback to the deferred, shortcut for
293         ``deferred.then(null, failCallback)``.
294
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
298         avoided as well.
299
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`
304
305     .. js:function:: Deferred.promise()
306
307         Returns a read-only view of the deferred object, with all
308         mutators (resolve and reject) methods removed.
309
310     .. js:function:: Deferred.resolve(value…)
311
312         Called to resolve a deferred, any value provided will be
313         passed onto the success handlers of the deferred object.
314
315         Resolving a deferred which has already been resolved or
316         rejected has no effect.
317
318     .. js:function:: Deferred.reject(value…)
319
320         Called to reject (fail) a deferred, any value provided will be
321         passed onto the failure handler of the deferred object.
322
323         Rejecting a deferred which has already been resolved or
324         rejected has no effect.
325
326 .. [#] or simply calling :js:class:`Deferred` as a function, the
327        result is the same
328
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).
334
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
338        case.
339
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