summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js
diff options
context:
space:
mode:
authorMartin Geubelle <mge@odoo.com>2018-06-22 13:24:13 +0200
committerChristophe Matthieu <chm@odoo.com>2018-08-13 17:09:28 +0200
commit7f4b4f86bdb8e8b70696a54f22ac64d2c549aeef (patch)
treeafcdcd2d1d86b8df97f795537ec624b66822d0f0 /addons/web/static/src/js
parent971629b9fe40911c3a9765c9fb746ea1c55d3560 (diff)
[IMP] web: add MutexedDropPrevious in concurrency
This new helper is meant to solve the following use case: serialize a number of concurrent rpcs while skipping the rpcs that arrive in between the mutex.
Diffstat (limited to 'addons/web/static/src/js')
-rw-r--r--addons/web/static/src/js/core/concurrency.js66
1 files changed, 66 insertions, 0 deletions
diff --git a/addons/web/static/src/js/core/concurrency.js b/addons/web/static/src/js/core/concurrency.js
index aeef29809d9..20247d1c483 100644
--- a/addons/web/static/src/js/core/concurrency.js
+++ b/addons/web/static/src/js/core/concurrency.js
@@ -220,6 +220,72 @@ return {
}
}),
/**
+ * A MutexedDropPrevious is a primitive for serializing computations while
+ * skipping the ones that where executed between a current one and before
+ * the execution of a new one. This is useful to avoid useless RPCs.
+ *
+ * You can read the Mutex description to understand its role ; for the
+ * DropPrevious part of this abstraction, imagine the following situation:
+ * you have a code that call the server with a fixed argument and a list of
+ * operations that only grows after each call and you only care about the
+ * RPC result (the server code doesn't do anything). If this code is called
+ * three times (A B C) and C is executed before B has started, it's useless
+ * to make an extra RPC (B) if you know that it won't have an impact and you
+ * won't use its result.
+ *
+ * Note that the promise returned by the exec call won't be resolved if
+ * exec is called before the first exec call resolution ; only the promise
+ * returned by the last exec call will be resolved (the other are rejected);
+ *
+ * A MutexedDropPrevious has to be a class, and not a function, because we
+ * have to keep track of some internal state. The exec function takes as
+ * argument an action (and not a deferred as DropPrevious for example)
+ * because it's the MutexedDropPrevious role to trigger the RPC call that
+ * returns a deferred when it's time.
+ */
+ MutexedDropPrevious: Class.extend({
+ init: function () {
+ this.currentDef = null;
+ this.locked = false;
+ this.pendingAction = null;
+ this.pendingDef = null;
+ },
+ /**
+ * @param {function} action a function which may return a deferred
+ * @returns {Deferred}
+ */
+ exec: function (action) {
+ var self = this;
+ if (this.locked) {
+ this.pendingAction = action;
+ var oldPendingDef = this.pendingDef;
+ var pendingDef = this.pendingDef = $.Deferred();
+ if (oldPendingDef) {
+ oldPendingDef.reject();
+ }
+ this.currentDef.reject();
+ return pendingDef.promise();
+ } else {
+ this.locked = true;
+ this.currentDef = $.Deferred();
+ $.when(action())
+ .then(this.currentDef.resolve.bind(this.currentDef))
+ .fail(this.currentDef.reject.bind(this.currentDef))
+ .always(function () {
+ self.locked = false;
+ if (self.pendingAction) {
+ var action = self.pendingAction;
+ self.pendingAction = null;
+ self.exec(action)
+ .then(self.pendingDef.resolve.bind(self.pendingDef))
+ .fail(self.pendingDef.reject.bind(self.pendingDef));
+ }
+ });
+ return this.currentDef.promise();
+ }
+ },
+ }),
+ /**
* Rejects a deferred as soon as a reference deferred is either resolved or
* rejected
*