[courier] resolve looper iteration when requests are aborted

The search looper returns the promise from search executions to ensure that subsequent loops do not run until the previous loop has completed. This causes the looper to get stuck when all of the aborted requests are completed, as completed requests do not resolve or reject their promises, causing the looper to never allow another search loop. Since this behavior is desirable for pages where we don't want to execute the `.then()` or `.catch()` callbacks for requests that were aborted, we work around it in this specific case by creating a promise that is resolved when each request is either aborted or completed.
This commit is contained in:
spalger 2017-02-15 23:01:20 -07:00
parent 9b39a19856
commit 1683e03b53
5 changed files with 22 additions and 9 deletions

View file

@ -185,7 +185,7 @@ export default function SourceAbstractFactory(Private, Promise, PromiseEmitter)
courierFetch.these([req]);
return req.defer.promise;
return req.getCompletePromise();
};
/**

View file

@ -175,7 +175,7 @@ export default function SearchSourceFactory(Promise, Private, config) {
// return promises created by the completion handler so that
// errors will bubble properly
return req.defer.promise.then(addRequest);
return req.getCompletePromise().then(addRequest);
});
};

View file

@ -23,7 +23,7 @@ export default function fetchService(Private, Promise) {
const fetchTheseSoon = (requests) => {
requests.forEach(req => req._setFetchRequested());
debouncedFetchThese();
return Promise.all(requests.map(req => req.defer.promise));
return Promise.all(requests.map(req => req.getCompletePromise()));
};
this.fetchQueued = (strategy) => {

View file

@ -1,5 +1,6 @@
import _ from 'lodash';
import moment from 'moment';
import { race } from 'bluebird';
import errors from 'ui/errors';
@ -14,7 +15,7 @@ export default function AbstractReqProvider(Private, Promise) {
constructor(source, defer) {
this.source = source;
this.defer = defer || Promise.defer();
this._whenAbortedHandlers = [];
this.abortedDefer = Promise.defer();
requestQueue.push(this);
}
@ -120,11 +121,12 @@ export default function AbstractReqProvider(Private, Promise) {
this._markStopped();
this.defer = null;
this.aborted = true;
_.callEach(this._whenAbortedHandlers);
this.abortedDefer.resolve();
this.abortedDefer = null;
}
whenAborted(cb) {
this._whenAbortedHandlers.push(cb);
this.abortedDefer.promise.then(cb);
}
complete() {
@ -133,6 +135,14 @@ export default function AbstractReqProvider(Private, Promise) {
this.defer.resolve(this.resp);
}
getCompletePromise() {
return this.defer.promise;
}
getCompleteOrAbortedPromise() {
return race([ this.defer.promise, this.abortedDefer.promise ]);
}
clone() {
return new this.constructor(this.source, this.defer);
}

View file

@ -17,9 +17,12 @@ export default function SearchLooperService(Private, Promise, Notifier, $rootSco
*/
const searchLooper = new Looper(null, function () {
$rootScope.$broadcast('courier:searchRefresh');
return fetch.these(
requestQueue.getInactive(searchStrategy)
);
const requests = requestQueue.getInactive(searchStrategy);
// promise returned from fetch.these() only resolves when
// the requests complete, but we want to continue even if
// the requests abort so we make our own
fetch.these(requests);
return Promise.all(requests.map(r => r.getCompleteOrAbortedPromise()));
});
searchLooper.onHastyLoop = function () {