简体   繁体   中英

How to avoid HTTP-request race conditions in JavaScript/AngularJS?

I'm using JavaScript in combination with AngularJS and recently went into some issues with HTTP-request race conditions.

For demo purposes i'll try to simplify the problem a bit: Say you have several scope.$watch listeners up and running and some of them may trigger from time to time some REST-calls.

Now there may be some logic going on withing these listeners triggering each other and in the end you end up with two REST-calls being made to the same url with different parameters while the parameters of the first call are obsolete in the meantime (all of this happens within some milliseconds). Both callbacks are essentially the same as well and will overwrite some variable "result" in my controller or whatever.

As long as both requests take the same amount of time on the server they will return in the same order and therefore the result of the second calls will overwrite the one of the first call and everything is fine. However, of course we can not guarantee that to happen, so we end up with an ugly race condition here.

So, now i'm wondering if there are any "best-practices" or "design-patterns" or other "tricks" to avoid these kind of problems in the first place.

I've read about canceling ajax requests and tried that solution - unfortunately it isn't as easy in AngularJS as it is in pure jQuery, plus the structure of my application didn't really favor this kind of solution. Yet, even if i go with ajax canceling i would still be interested in a corresponding design pattern, since canceling pending requests isn't always what you want to do in you application.

For my project a working solution was to remember what the last request parameters were whenever i did such a problematic call and only resolve the result if and only if the last used request parameters were equal to the request parameters used for the given call, so in other words:

  • call f with param=1 --> remember 1
  • call f with param=2 --> remember 2, hence overwrite 1
  • 2nd call returns --> check if used param (2) equals last used param (2) --> resolve
  • 1st call returns --> check if used param (1) equals last used param (1) --> don't resolve

Well, this worked for my program in my context, but of course it doesn't always apply. Sometimes you need to have the option to make two calls of the same kind with two different parameters at the same time without your code rejecting it...

So again: do you guys know any good solutions to this problem in the first place ? Enlight me please... :D

I would use promise chaining .

  • call f with param=1 --> remember 1
  • call f with param=2 --> chain from 1

Then scope will be updated by 2 only after 1 resolves.

var httpPromise1 = $http(params1).then ( function onFulfilled(response) {
    $scope.data = response.data; //data set #1
});

//start second request
var startXHRpromise = $http(params2);

//chain fulFillment
var httpPromise2 = httpPromise1.then (function() {
    //chain with started XHR promise
    return startXHRpromise; 
}).then (function onFulfilled(response) {
    $scope.data = response.data; //data set #2
});

By chaining promises, the second data set is put on scope only after the first data set.

Since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs. 1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM