In our application, we have an search input field. Typically a request is sent while the user types (a la Google Instant) and the results are displayed.
Obviously, the following can happen:
- User types, which results in ajaxRequest1
- User continues typing, resulting in ajaxRequest2
- results2 corresponding to ajaxRequest2 are received and displayed
- After this, results1 corresponding to ajaxRequest1 are received. Obviously, since ajaxRequest2 was sent after ajaxRequest1, we only care about results2, not results1.
EDIT: The obvious answer here is "Use debounce". For reasons of confidentiality and brevity, I'll just say here that it won't work in our particular scenario. I know what debounce does and I have considered it.
In pseudo-code, we used to handle it like this:
$scope.onInput = function() {
var inputText = getInput();
SearchService.search(inputText).then(function(results) {
// only display if input hasn't changed since request was sent
if(inputText === getInput()) {
displayResults(results);
}
});
};
Since this involves a lot of boilerplate and looks ugly, we moved to a pattern where the SearchService
manages things a bit better
$scope.onInput = function() {
var inputText = getInput();
SearchService.search(inputText).then(function(results) {
displayResults(results);
});
}
function SearchService() {
var cachedSearchDeferred;
this.search = function(inputText) {
if(cachedSearchDeferred) {
//means there's an unresolved promise corresponding to an older request
cachedSearchDeferred.reject();
}
var deferred = $q.deferred();
$http.post(...).then(function(response) {
// saves us having to check the deferred's state outside
cachedSearchDeferred = null;
deferred.resolve(response.data);
});
cachedSearchDeferred = deferred;
return deferred.promise;
}
}
This works fine. The SearchService
creates a deferred containing the promise corresponding to the most recent call to SearchService.search
. If another call is made to SearchService.search
the old deferred is rejected and a new deferred is created corresponding to the new call.
Two questions:
- Is this a good pattern to do what we need - essentially request locking? We want to ensure that only the most recent request's promise resolves successfully
- If we had other
SearchService
methods that needed to behave similarly, then this deferred boilerplate needs to be inside every method. Is there a better way?
Aucun commentaire:
Enregistrer un commentaire