diff options
| author | Robot Odoo <xmo+roboto@odoo.com> | 2019-03-06 21:18:36 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-03-06 21:18:36 +0100 |
| commit | fc60c49ba3cb30fc4e436c9ffc1661d707fe858c (patch) | |
| tree | 110bf47078ce4862cfad05aa061ba1238ebc99e6 /doc/reference | |
| parent | 414ac1a49a61e7f68b4c8dab8688a5b8840aefed (diff) | |
| parent | 53e7050ce0c1bd873f239bce8ad81ba42c19d57a (diff) | |
[MERGE] web,*: update code base to jQuery 3.*
The goal of this task is to update jQuery from version 1.11 to 3.x.
Task-id : 1896658
As we are skipping 2 magor versions, there are some breaking changes in the jquery library described here: https://jquery.com/upgrade-guide/3.0/
Main changes are:
-------------------------
1. Update the libraries jQuery, jQueryUI, QUnit and select2 to their latest version.
The link to generate the build of jQueryUI with the used components: [jquery UI build](https://jqueryui.com/download/#!version=1.12.1&components=111110010101111110100010110000011010000000000000)
2. Use native promises ([a complete explanation about promises here](https://github.com/getify/You-Dont-Know-JS/blob/master/async%20%26%20performance/ch3.md)) everywhere possible:
In order to [be A+ compliant](https://promisesaplus.com/), the jq3 deferred uses `setTimeout(..., 0)`
for every then and catch, which takes between 4 and 10 ms by call.
This would be way too slow to use in Odoo.
To prevent this, we have used the native Promises everywhere possible.
Be it with jq deferred or native promises, all promises are now asynchronous,
this example will ALWAYS result in printing first "just after blabla" then "blabla"
because the `then` function will always be asynchronous (opposite to
what jquery used to do)
```
function () {
Promise.resolve("blabla").then(console.log);
console.log("just after blabla");
}
// console:
// just after blabla
// blabla
```
3. On native promises, you should see the catch like a try/catch in
javascript, meaning that it will catch all errors including coding errors:
For this we have introduced the concept of `guardedCatch`, which will be executed
on promise rejection unless the promise is rejected because there is
a code error or runtime error (calls on undefined, etc).
4. Native Promises do not have `done`, `fail` and `always` methods nor does it have the `state` method
- `fail` can be replaced with `guardedCatch`
- `return $.when(x).done(function() ...)` can be replaced with
```
var x = Promise.resolve(); // we get the x promise in a local variable, without then
x.then(function() ...); // we add a new handler to x. This operation returns a new promise we don't care about
return x; // we return the x promise that will be completed without waiting for the then
```
- `$.when().done(function ...)` without the return can be replaced by `then(function ....)`, because if it is not returned, it doesn't matter what is chained or not `$.when().always(function() ...)` can be replaced with
```
var always = function() ...
Promise.resolve().then(always).guardedCatch(always);
```
- It is not possible to inspect the state of a promise, except in debug tools of chrome and firefox. Any code that relies on `$.Deferred.state()` should be rewritten to use flags in `then` and `catch` handlers. Remember that it is not possible to execute the handlers in a synchroneous manner, so when a promise is created, its `then` will never be called on the same execution stack. At best it will be called on the next microtask tick (before the next setTimeout).
5. We have introduced the concept of guardedCatch, to copy the behavior of the old fail:
The guarded catch will only execute the handler in parameter if the rejection reason is not an `Error`
```
var failingPromise = Promise.reject({message:"I am the rejection reason"});
failingPromise.guardedCatch(function(reason) {console.log(reason);}); // is called, then returns a rejected promise
failingPromise.catch(function(reason) {console.log(reason);}); // is called
```
will write the reason "I am the rejection reason" to the console twice: once because the `guardedCatch` will execute (and return a rejected `Promise` with the same reason it was called), a second time because the `catch` will catch the rejected reason returned by the `guardedCatch`
```
var promiseWithError = Promise.resolve().then(function() {
a.b = 0; // <-- the variable a does not exist, so a.b is an error
});
promiseWithError.guardedCatch(function(reason) {console.log(reason);}); // will not be called
promiseWithError.catch(function(reason) {console.log(reason);}); // will be called
```
contrary to how deferred used to work, here the catch will be called because it behaves like a `try... catch...` and will catch the coding error like if it was a business error. The `guardedCatch` however does not call the handler if the rejection reason is an error.
In all Odoo code, we never reject a promise with an error and always use objects to defined the rejected reason, so it is a good idea to use `guardedCatch` everywhere.
6. Native Promises can have only one parameter passed to a `then` function or a `catch` function:
`xxx.then(function(param1, param2) {...}); // param2 will never have a value`
- we can replace `$.when(prom1, prom2).then(function(result1, result2) { ... });` by
```
Promise.all([prom1, prom2]).then(function(results) {
var result1 = results[1];
var result2 = results[2];
...
});
```
7. We are now using ES7 code in the unit tests (async await).
closes odoo/odoo#28457
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
Co-authored-by: Aaron Bohy <aab@odoo.com>
Co-authored-by: Christophe Matthieu <chm@odoo.com>
Co-authored-by: Mathieu Duckerts-Antoine <dam@odoo.com>
Co-authored-by: David Monjoie <dmo@odoo.com>
Co-authored-by: Martin Geubelle <mge@odoo.com>
Co-authored-by: svs-odoo <svs@odoo.com>
Co-authored-by: Vincent Schippefilt <vsc@odoo.com>
Diffstat (limited to 'doc/reference')
| -rw-r--r-- | doc/reference/async.rst | 341 | ||||
| -rw-r--r-- | doc/reference/javascript_reference.rst | 14 |
2 files changed, 7 insertions, 348 deletions
diff --git a/doc/reference/async.rst b/doc/reference/async.rst deleted file mode 100644 index d8d9c7db90f..00000000000 --- a/doc/reference/async.rst +++ /dev/null @@ -1,341 +0,0 @@ -:orphan: - -.. _reference/async: - -Asynchronous Operations -======================= - -As a language (and runtime), javascript is fundamentally -single-threaded. This means any blocking request or computation will -block the whole page (and, in older browsers, the software itself -even preventing users from switching to another 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. - -The goal of this guide is to provide some tools to deal with -asynchronous systems, and warn against systemic issues or dangers. - -Deferreds ---------- - -Deferreds are a form of `promises`_. OpenERP Web currently uses -`jQuery's deferred`_. - -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 -<reference/async/composition>`. - -Using deferreds -~~~~~~~~~~~~~~~ - -Deferreds's most important method is :js:func:`Deferred.then`. It 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). - -.. _reference/async/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 -multiple 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. - -The 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: with the tools described so far, handling 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. - -But :js:func:`~Deferred.then` also allows handling this kind of -chains: it returns a new promise object, not the one it was called -with, and the return values of the callbacks is important to this behavior: -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.then`'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.then(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).then(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.then` 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). - - Returns a new deferred which resolves to the result of the - corresponding callback, if a callback returns a deferred - itself that new deferred will be used as the resolution of the - chain. - - :param doneCallback: function called when the deferred is resolved - :param failCallback: function called when the deferred is rejected - :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)``. - - .. note:: a difference is the result of :js:func:`Deferred.done`'s - is ignored rather than forwarded through the chain - - 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. - -.. [#] 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 diff --git a/doc/reference/javascript_reference.rst b/doc/reference/javascript_reference.rst index 059b5796041..02c477905d5 100644 --- a/doc/reference/javascript_reference.rst +++ b/doc/reference/javascript_reference.rst @@ -879,7 +879,7 @@ Here is an example on how this event system could be used: this.counter = new Counter(this); this.counter.on('valuechange', this, this._onValueChange); var def = this.counter.appendTo(this.$el); - return $.when(def, this._super.apply(this, arguments); + return Promise.all([def, this._super.apply(this, arguments)]); }, _onValueChange: function (val) { // do something with val @@ -1500,15 +1500,15 @@ order. - type: setting the input type (*text* by default, can be set on *number*) - On edit mode, the field is rendered as an input with the HTML attribute type - setted on *number* (so user can benefit the native support, especially on + On edit mode, the field is rendered as an input with the HTML attribute type + setted on *number* (so user can benefit the native support, especially on mobile). In this case, the default formatting is disabled to avoid incompability. .. code-block:: xml <field name="int_value" options='{"type": "number"}'/> - - step: set the step to the value up and down when the user click on buttons + - step: set the step to the value up and down when the user click on buttons (only for input of type number, 1 by default) .. code-block:: xml @@ -1532,15 +1532,15 @@ order. - type: setting the input type (*text* by default, can be set on *number*) - On edit mode, the field is rendered as an input with the HTML attribute type - setted on *number* (so user can benefit the native support, especially on + On edit mode, the field is rendered as an input with the HTML attribute type + setted on *number* (so user can benefit the native support, especially on mobile). In this case, the default formatting is disabled to avoid incompability. .. code-block:: xml <field name="int_value" options='{"type": "number"}'/> - - step: set the step to the value up and down when the user click on buttons + - step: set the step to the value up and down when the user click on buttons (only for input of type number, 1 by default) .. code-block:: xml |
