Commit 3b37289a by benjaminapetersen

Add opts.skipDigest for DataService.watch

- provides mechanism to keep WebSockets from spamming the $digest loop
- flips invokeApply passed to the internal $timeout calls,
  stopping a $digest loop & avoiding a render.
- DataService._watchOpOnMessage also honors the skipDigest flag,
  which is plumbed down through .watch() -> .startListOp -> ._listOpComplete -> ._startWatchOp -> ._watchOpOnMessage

example usage:

DataService.watch(resource, context, (data) => {
  // optionally call $applyAsync here in controlling code
  // to manually run a $digest
  $scope.$applyAsync(() => {
   // do stuff with data here
  });
}, { skipDigest: true });

// or Debounce
DataService.watch(resource, context, _.debounce(() => {
 // $apply here, inside debounce
}, 400), { skipDigest: true });
parent 27a8f85a
......@@ -1736,6 +1736,9 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
// pollInterval: in milliseconds, how long to wait between polling the server
// only applies if poll=true. Default is 5000.
// http: similar to .get, etc. at this point, only used to pass http.params for filtering
// skipDigest: will skip the $apply & avoid triggering a digest loop
// if set to `true`. Is intentionally the inverse of the invokeApply
// arg passed to $timeout (due to default values).
// errorNotification: will popup an error notification if the API request fails (default true)
// returns handle to the watch, needed to unwatch e.g.
// var handle = DataService.watch(resource,context,callback[,opts])
......@@ -1743,6 +1746,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
DataService.prototype.watch = function(resource, context, callback, opts) {
resource = APIService.toResourceGroupVersion(resource);
opts = opts || {};
var invokeApply = !opts.skipDigest;
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
if (callback) {
// If we were given a callback, add it
......@@ -1770,7 +1774,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
if (callback) {
$timeout(function() {
callback(self._data(key));
}, 0);
}, 0, invokeApply);
}
}
else {
......@@ -1782,7 +1786,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
if (resourceVersion === self._resourceVersion(key)) {
callback(self._data(key)); // but just in case, still pull from the current data map
}
}, 0);
}, 0, invokeApply);
}
}
if (!this._listInFlight(key)) {
......@@ -2245,6 +2249,8 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
DataService.prototype._watchOpOnMessage = function(resource, context, opts, event) {
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
opts = opts || {};
var invokeApply = !opts.skipDigest;
try {
var eventData = $.parseJSON(event.data);
......@@ -2274,7 +2280,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
// without timeout this is triggering a repeated digest loop
$timeout(function() {
self._watchCallbacks(key).fire(self._data(key), eventData.type, eventData.object);
}, 0);
}, 0, invokeApply);
}
catch (e) {
// TODO: surface in the UI?
......
......@@ -3502,6 +3502,9 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
// pollInterval: in milliseconds, how long to wait between polling the server
// only applies if poll=true. Default is 5000.
// http: similar to .get, etc. at this point, only used to pass http.params for filtering
// skipDigest: will skip the $apply & avoid triggering a digest loop
// if set to `true`. Is intentionally the inverse of the invokeApply
// arg passed to $timeout (due to default values).
// errorNotification: will popup an error notification if the API request fails (default true)
// returns handle to the watch, needed to unwatch e.g.
// var handle = DataService.watch(resource,context,callback[,opts])
......@@ -3509,6 +3512,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
DataService.prototype.watch = function(resource, context, callback, opts) {
resource = APIService.toResourceGroupVersion(resource);
opts = opts || {};
var invokeApply = !opts.skipDigest;
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
if (callback) {
// If we were given a callback, add it
......@@ -3536,7 +3540,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
if (callback) {
$timeout(function() {
callback(self._data(key));
}, 0);
}, 0, invokeApply);
}
}
else {
......@@ -3548,7 +3552,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
if (resourceVersion === self._resourceVersion(key)) {
callback(self._data(key)); // but just in case, still pull from the current data map
}
}, 0);
}, 0, invokeApply);
}
}
if (!this._listInFlight(key)) {
......@@ -4011,6 +4015,8 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
DataService.prototype._watchOpOnMessage = function(resource, context, opts, event) {
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
opts = opts || {};
var invokeApply = !opts.skipDigest;
try {
var eventData = $.parseJSON(event.data);
......@@ -4040,7 +4046,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
// without timeout this is triggering a repeated digest loop
$timeout(function() {
self._watchCallbacks(key).fire(self._data(key), eventData.type, eventData.object);
}, 0);
}, 0, invokeApply);
}
catch (e) {
// TODO: surface in the UI?
......
......@@ -1471,7 +1471,7 @@ ws.close();
};
}, DataService.prototype.watch = function(resource, context, callback, opts) {
resource = APIService.toResourceGroupVersion(resource), opts = opts || {};
var key = this._uniqueKey(resource, null, context, _.get(opts, "http.params"));
var invokeApply = !opts.skipDigest, key = this._uniqueKey(resource, null, context, _.get(opts, "http.params"));
if (callback) this._watchCallbacks(key).add(callback); else if (!this._watchCallbacks(key).has()) return {};
var existingWatchOpts = this._watchOptions(key);
if (existingWatchOpts) {
......@@ -1480,12 +1480,12 @@ if (!!existingWatchOpts.poll != !!opts.poll) throw "A watch already exists for "
var self = this;
if (this._isCached(key)) callback && $timeout(function() {
callback(self._data(key));
}, 0); else {
}, 0, invokeApply); else {
if (callback) {
var resourceVersion = this._resourceVersion(key);
this._data(key) && $timeout(function() {
resourceVersion === self._resourceVersion(key) && callback(self._data(key));
}, 0);
}, 0, invokeApply);
}
this._listInFlight(key) || this._startListOp(resource, context, opts);
}
......@@ -1645,6 +1645,8 @@ var key = this._uniqueKey(resource, null, context, _.get(opts, "http.params"));
this._addWebsocketEvent(key, "open");
}, DataService.prototype._watchOpOnMessage = function(resource, context, opts, event) {
var key = this._uniqueKey(resource, null, context, _.get(opts, "http.params"));
opts = opts || {};
var invokeApply = !opts.skipDigest;
try {
var eventData = $.parseJSON(event.data);
if ("ERROR" == eventData.type) return Logger.log("Watch window expired for resource/context", resource, context), void (event.target && (event.target.shouldRelist = !0));
......@@ -1652,7 +1654,7 @@ if ("ERROR" == eventData.type) return Logger.log("Watch window expired for resou
var self = this;
$timeout(function() {
self._watchCallbacks(key).fire(self._data(key), eventData.type, eventData.object);
}, 0);
}, 0, invokeApply);
} catch (e) {
Logger.error("Error processing message", resource, event.data);
}
......
......@@ -602,6 +602,9 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
// pollInterval: in milliseconds, how long to wait between polling the server
// only applies if poll=true. Default is 5000.
// http: similar to .get, etc. at this point, only used to pass http.params for filtering
// skipDigest: will skip the $apply & avoid triggering a digest loop
// if set to `true`. Is intentionally the inverse of the invokeApply
// arg passed to $timeout (due to default values).
// errorNotification: will popup an error notification if the API request fails (default true)
// returns handle to the watch, needed to unwatch e.g.
// var handle = DataService.watch(resource,context,callback[,opts])
......@@ -609,6 +612,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
DataService.prototype.watch = function(resource, context, callback, opts) {
resource = APIService.toResourceGroupVersion(resource);
opts = opts || {};
var invokeApply = !opts.skipDigest;
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
if (callback) {
// If we were given a callback, add it
......@@ -636,7 +640,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
if (callback) {
$timeout(function() {
callback(self._data(key));
}, 0);
}, 0, invokeApply);
}
}
else {
......@@ -648,7 +652,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
if (resourceVersion === self._resourceVersion(key)) {
callback(self._data(key)); // but just in case, still pull from the current data map
}
}, 0);
}, 0, invokeApply);
}
}
if (!this._listInFlight(key)) {
......@@ -1111,6 +1115,8 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
DataService.prototype._watchOpOnMessage = function(resource, context, opts, event) {
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
opts = opts || {};
var invokeApply = !opts.skipDigest;
try {
var eventData = $.parseJSON(event.data);
......@@ -1140,7 +1146,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
// without timeout this is triggering a repeated digest loop
$timeout(function() {
self._watchCallbacks(key).fire(self._data(key), eventData.type, eventData.object);
}, 0);
}, 0, invokeApply);
}
catch (e) {
// TODO: surface in the UI?
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment