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
