Order promises in AngularJS

Question:

Is there an “easy” way to undo ( $q - / $http -) promises in AngularJS or determine the order in which promises are allowed?

Example

I have a long calculation and I am requesting the result via $http . Some actions or events require me to restart the calculation (and thus send a new $http request) before the initial promise is resolved. Thus, I think I can not use a simple implementation, for example

 $http.post().then(function(){ //apply data to view }) 

because I cannot guarantee that the responses are returned in the order in which I sent the requests. In the end, I want to show the result of the last calculation when all promises were correctly resolved.

However, I would like to avoid waiting for the first response until I submit a new request, for example:

 const timeExpensiveCalculation = function(){ return $http.post().then(function(response){ if (isNewCalculationChained) {return timeExpensiveCalculation();} else {return response.data;} }) } 

Thoughts:

When using $http I can access the configuration object in the response to use some timestamps or other identifiers to manually order incoming responses. However, I was hoping that I could just say angular somehow cancel the deprecated promise and thus not run the .then () function when it is resolved.

This does not work without a manual implementation for $q - promises instead of $http .

Maybe just abandoning the promise right away is the way to go? But in both cases, this can go on forever until, finally, the promise is resolved before the next request is formed (which leads to an empty view in the meantime).

Is there some kind of angular API function that I will miss, or are there robust design patterns or “tricks” with a promise chain or $ q.all to handle multiple promises that return “the same” data?

+5
source share
4 answers

I do this by creating requestId , and in the promise then() function, I check if the response comes from the last requestId .

Although this approach does not actually cancel out previous promises, it provides a quick and easy way to ensure that you are handling the most recent response request.

Sort of:

 var activeRequest; function doRequest(params){ // requestId is the id for the request being made in this function call var requestId = angular.toJson(params); // I usually md5 hash this // activeRequest will always be the last requestId sent out activeRequest = requestId; $http.get('/api/something', {data: params}) .then(function(res){ if(activeRequest == requestId){ // this is the response for last request // activeRequest is now handled, so clear it out activeRequest = undefined; } else { // response from previous request (typically gets ignored) } }); } 

Edit: On the other hand, I would like to add that this requestId's tracking concept can also be applied to prevent duplicate requests. For example, in my Data service load(module, id) method load(module, id) I execute a small process as follows:

  • generate requestId based on URL + parameters.
  • check request hash table for requestId

    • if requestId not found: generate a new request and store the promise in a hash table
    • if requestId found: just return the promise from the hash table
  • When the request completes, delete the requestId entry from the hash table.

+3
source

Canceling a promise simply causes it not to call the onFulfilled and onRejected at the then stage. Since @ user2263572 mentioned that it’s always better to refuse a promise that has not been canceled (ES6 native promises cannot be canceled in any case) and process this condition on it then stage (for example, ignoring a task if the global variable is set to 2 as shown in the next snippet), and I'm sure you can find many other ways to do this. One example would be:

Sorry, I'm using v (looks like a control character) for resolve and x (obviously) for reject functions.

 var prom1 = new Promise((v,x) => setTimeout(v.bind(null,"You shall not read this"),2000)), prom2, validPromise = 1; prom1.then(val => validPromise === 1 && console.log(val)); // oh what have i done..!?! Now i have to fire a new promise prom2 = new Promise((v,x) => setTimeout(v.bind(null,"This is what you will see"),3000)); validPromise = 2; prom2.then(val => validPromise === 2 && console.log(val)); 
+4
source

I'm still trying to find a good way to unit test, but you can try this strategy:

 var canceller = $q.defer(); service.sendCalculationRequest = function () { canceller.resolve(); return $http({ method: 'GET', url: '/do-calculation', timeout: canceller.promise }); }; 
0
source

In ECMA6 promises, there is a Promise.race(promiseArray) method . As an argument, it takes an array of promises and returns one promise. The first promise to allow in the array will pass its allowed value to the .then returned promise, while the other promises array, which came second, etc., will not be expected.

Example:

 var httpCall1 = $http.get('/api/something', {data: params}) .then(function(val) { return { id: "httpCall1" val: val } }) var httpCall2 = $http.get('/api/something-else', {data: params}) .then(function(val) { return { id: "httpCall2" val: val } }) // Might want to make a reusable function out of the above two, if you use this in Production Promise.race([httpCall1, httpCall2]) .then(function(winningPromise) { console.log('And the winner is ' + winningPromise.id); doSomethingWith(winningPromise.val); }); 

You can use this with a promise policy, or look at q.race what someone has developed for Angular (although I haven’t tested it).

0
source

All Articles