diff options
| author | Antony Lesuisse <al@openerp.com> | 2012-10-06 19:10:53 +0200 |
|---|---|---|
| committer | Antony Lesuisse <al@openerp.com> | 2012-10-06 19:10:53 +0200 |
| commit | bc3cf0b74ff033d247a826ba55b608922454548a (patch) | |
| tree | 50ea97df74a07e3921ce13238abb5a9b1f107470 /doc/async.rst | |
| parent | 55bcd978af814c90b1d486055940f242dfe0dbde (diff) | |
rm files, move postload hook
bzr revid: al@openerp.com-20121006171053-qggp1r5263h7v7vd
Diffstat (limited to 'doc/async.rst')
| -rw-r--r-- | doc/async.rst | 353 |
1 files changed, 0 insertions, 353 deletions
diff --git a/doc/async.rst b/doc/async.rst deleted file mode 100644 index 23b3409bd8f..00000000000 --- a/doc/async.rst +++ /dev/null @@ -1,353 +0,0 @@ -Don't stop the world now: asynchronous development and Javascript -================================================================= - -As a language (and runtime), javascript is fundamentally -single-threaded. This means any blocking request or computation will -blocks the whole page (and, in older browsers, the software itself -even preventing users from switching to an other tab): a javascript -environment can be seen as an event-based runloop where application -developers have no control over the runloop itself. - -As a result, performing long-running synchronous network requests or -other types of complex and expensive accesses is frowned upon and -asynchronous APIs are used instead. - -Asynchronous code rarely comes naturally, especially for developers -used to synchronous server-side code (in Python, Java or C#) where the -code will just block until the deed is gone. This is increased further -when asynchronous programming is not a first-class concept and is -instead implemented on top of callbacks-based programming, which is -the case in javascript. - -The goal of this guide is to provide some tools to deal with -asynchronous systems, and warn against systematic issues or dangers. - -Deferreds ---------- - -Deferreds are a form of `promises`_. OpenERP Web currently uses -`jQuery's deferred`_, but any `CommonJS Promises/A`_ implementation -should work. - -The core idea of deferreds is that potentially asynchronous methods -will return a :js:class:`Deferred` object instead of an arbitrary -value or (most commonly) nothing. - -This object can then be used to track the end of the asynchronous -operation by adding callbacks onto it, either success callbacks or -error callbacks. - -A great advantage of deferreds over simply passing callback functions -directly to asynchronous methods is the ability to :ref:`compose them -<deferred-composition>`. - -Using deferreds -~~~~~~~~~~~~~~~ - -`CommonJS Promises/A`_ deferreds have only one method of importance: -:js:func:`Deferred.then`. This method is used to attach new callbacks -to the deferred object. - -* the first parameter attaches a success callback, called when the - deferred object is successfully resolved and provided with the - resolved value(s) for the asynchronous operation. - -* the second parameter attaches a failure callback, called when the - deferred object is rejected and provided with rejection values - (often some sort of error message). - -Callbacks attached to deferreds are never "lost": if a callback is -attached to an already resolved or rejected deferred, the callback -will be called (or ignored) immediately. A deferred is also only ever -resolved or rejected once, and is either resolved or rejected: a given -deferred can not call a single success callback twice, or call both a -success and a failure callbacks. - -:js:func:`~Deferred.then` should be the method you'll use most often -when interacting with deferred objects (and thus asynchronous APIs). - -Building deferreds -~~~~~~~~~~~~~~~~~~ - -After using asynchronous APIs may come the time to build them: for -`mocks`_, to compose deferreds from multiple source in a complex -manner, in order to let the current operations repaint the screen or -give other events the time to unfold, ... - -This is easy using jQuery's deferred objects. - -.. note:: this section is an implementation detail of jQuery Deferred - objects, the creation of promises is not part of any - standard (even tentative) that I know of. If you are using - deferred objects which are not jQuery's, their API may (and - often will) be completely different. - -Deferreds are created by invoking their constructor [#]_ without any -argument. This creates a :js:class:`Deferred` instance object with the -following methods: - -:js:func:`Deferred.resolve` - - As its name indicates, this method moves the deferred to the - "Resolved" state. It can be provided as many arguments as - necessary, these arguments will be provided to any pending success - callback. - -:js:func:`Deferred.reject` - - Similar to :js:func:`~Deferred.resolve`, but moves the deferred to - the "Rejected" state and calls pending failure handlers. - -:js:func:`Deferred.promise` - - Creates a readonly view of the deferred object. It is generally a - good idea to return a promise view of the deferred to prevent - callers from resolving or rejecting the deferred in your stead. - -:js:func:`~Deferred.reject` and :js:func:`~Deferred.resolve` are used -to inform callers that the asynchronous operation has failed (or -succeeded). These methods should simply be called when the -asynchronous operation has ended, to notify anybody interested in its -result(s). - -.. _deferred-composition: - -Composing deferreds -~~~~~~~~~~~~~~~~~~~ - -What we've seen so far is pretty nice, but mostly doable by passing -functions to other functions (well adding functions post-facto would -probably be a chore... still, doable). - -Deferreds truly shine when code needs to compose asynchronous -operations in some way or other, as they can be used as a basis for -such composition. - -There are two main forms of compositions over deferred: multiplexing -and piping/cascading. - -Deferred multiplexing -````````````````````` - -The most common reason for multiplexing deferred is simply performing -2+ asynchronous operations and wanting to wait until all of them are -done before moving on (and executing more stuff). - -The jQuery multiplexing function for promises is :js:func:`when`. - -.. note:: the multiplexing behavior of jQuery's :js:func:`when` is an - (incompatible, mostly) extension of the behavior defined in - `CommonJS Promises/B`_. - -This function can take any number of promises [#]_ and will return a -promise. - -This returned promise will be resolved when *all* multiplexed promises -are resolved, and will be rejected as soon as one of the multiplexed -promises is rejected (it behaves like Python's ``all()``, but with -promise objects instead of boolean-ish). - -The resolved values of the various promises multiplexed via -:js:func:`when` are mapped to the arguments of :js:func:`when`'s -success callback, if they are needed. The resolved values of a promise -are at the same index in the callback's arguments as the promise in -the :js:func:`when` call so you will have: - -.. code-block:: javascript - - $.when(p0, p1, p2, p3).then( - function (results0, results1, results2, results3) { - // code - }); - -.. warning:: - - in a normal mapping, each parameter to the callback would be an - array: each promise is conceptually resolved with an array of 0..n - values and these values are passed to :js:func:`when`'s - callback. But jQuery treats deferreds resolving a single value - specially, and "unwraps" that value. - - For instance, in the code block above if the index of each promise - is the number of values it resolves (0 to 3), ``results0`` is an - empty array, ``results2`` is an array of 2 elements (a pair) but - ``results1`` is the actual value resolved by ``p1``, not an array. - -Deferred chaining -````````````````` - -A second useful composition is starting an asynchronous operation as -the result of an other asynchronous operation, and wanting the result -of both: :js:func:`Deferred.then` returns the deferred on which it was -called, so handle e.g. OpenERP's search/read sequence with this would -require something along the lines of: - -.. code-block:: javascript - - var result = $.Deferred(); - Model.search(condition).then(function (ids) { - Model.read(ids, fields).then(function (records) { - result.resolve(records); - }); - }); - return result.promise(); - -While it doesn't look too bad for trivial code, this quickly gets -unwieldy. - -Instead, jQuery provides a tool to handle this kind of chains: -:js:func:`Deferred.pipe`. - -:js:func:`~Deferred.pipe` has the same signature as -:js:func:`~Deferred.then` and could be used in the same manner -provided its return value was not used. - -It differs from :js:func:`~Deferred.then` in two ways: it returns a -new promise object, not the one it was called with, and the return -values of the callbacks is actually important to it: whichever -callback is called, - -* If the callback is not set (not provided or left to null), the - resolution or rejection value(s) is simply forwarded to - :js:func:`~Deferred.pipe`'s promise (it's essentially a noop) - -* If the callback is set and does not return an observable object (a - deferred or a promise), the value it returns (``undefined`` if it - does not return anything) will replace the value it was given, e.g. - - .. code-block:: javascript - - promise.pipe(function () { - console.log('called'); - }); - - will resolve with the sole value ``undefined``. - -* If the callback is set and returns an observable object, that object - will be the actual resolution (and result) of the pipe. This means a - resolved promise from the failure callback will resolve the pipe, - and a failure promise from the success callback will reject the - pipe. - - This provides an easy way to chain operation successes, and the - previous piece of code can now be rewritten: - - .. code-block:: javascript - - return Model.search(condition).pipe(function (ids) { - return Model.read(ids, fields); - }); - - the result of the whole expression will encode failure if either - ``search`` or ``read`` fails (with the right rejection values), and - will be resolved with ``read``'s resolution values if the chain - executes correctly. - -:js:func:`~Deferred.pipe` is also useful to adapt third-party -promise-based APIs, in order to filter their resolution value counts -for instance (to take advantage of :js:func:`when` 's special treatment -of single-value promises). - -jQuery.Deferred API -~~~~~~~~~~~~~~~~~~~ - -.. js:function:: when(deferreds…) - - :param deferreds: deferred objects to multiplex - :returns: a multiplexed deferred - :rtype: :js:class:`Deferred` - -.. js:class:: Deferred - - .. js:function:: Deferred.then(doneCallback[, failCallback]) - - Attaches new callbacks to the resolution or rejection of the - deferred object. Callbacks are executed in the order they are - attached to the deferred. - - To provide only a failure callback, pass ``null`` as the - ``doneCallback``, to provide only a success callback the - second argument can just be ignored (and not passed at all). - - :param doneCallback: function called when the deferred is resolved - :type doneCallback: Function - :param failCallback: function called when the deferred is rejected - :type failCallback: Function - :returns: the deferred object on which it was called - :rtype: :js:class:`Deferred` - - .. js:function:: Deferred.done(doneCallback) - - Attaches a new success callback to the deferred, shortcut for - ``deferred.then(doneCallback)``. - - This is a jQuery extension to `CommonJS Promises/A`_ providing - little value over calling :js:func:`~Deferred.then` directly, - it should be avoided. - - :param doneCallback: function called when the deferred is resolved - :type doneCallback: Function - :returns: the deferred object on which it was called - :rtype: :js:class:`Deferred` - - .. js:function:: Deferred.fail(failCallback) - - Attaches a new failure callback to the deferred, shortcut for - ``deferred.then(null, failCallback)``. - - A second jQuery extension to `Promises/A <CommonJS - Promises/A>`_. Although it provides more value than - :js:func:`~Deferred.done`, it still is not much and should be - avoided as well. - - :param failCallback: function called when the deferred is rejected - :type failCallback: Function - :returns: the deferred object on which it was called - :rtype: :js:class:`Deferred` - - .. js:function:: Deferred.promise() - - Returns a read-only view of the deferred object, with all - mutators (resolve and reject) methods removed. - - .. js:function:: Deferred.resolve(value…) - - Called to resolve a deferred, any value provided will be - passed onto the success handlers of the deferred object. - - Resolving a deferred which has already been resolved or - rejected has no effect. - - .. js:function:: Deferred.reject(value…) - - Called to reject (fail) a deferred, any value provided will be - passed onto the failure handler of the deferred object. - - Rejecting a deferred which has already been resolved or - rejected has no effect. - - .. js:function:: Deferred.pipe(doneFilter[, failFilter]) - - Filters the result of a deferred, able to transform a success - into failure and a failure into success, or to delay - resolution further. - -.. [#] or simply calling :js:class:`Deferred` as a function, the - result is the same - -.. [#] or not-promises, the `CommonJS Promises/B`_ role of - :js:func:`when` is to be able to treat values and promises - uniformly: :js:func:`when` will pass promises through directly, - but non-promise values and objects will be transformed into a - resolved promise (resolving themselves with the value itself). - - jQuery's :js:func:`when` keeps this behavior making deferreds - easy to build from "static" values, or allowing defensive code - where expected promises are wrapped in :js:func:`when` just in - case. - -.. _promises: http://en.wikipedia.org/wiki/Promise_(programming) -.. _jQuery's deferred: http://api.jquery.com/category/deferred-object/ -.. _CommonJS Promises/A: http://wiki.commonjs.org/wiki/Promises/A -.. _CommonJS Promises/B: http://wiki.commonjs.org/wiki/Promises/B -.. _mocks: http://en.wikipedia.org/wiki/Mock_object |
