Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Order of promises in AngularJS

Question:

Is there an "easy" way to cancel ($q-/$http-)promises in AngularJS or determine the order in which promises were resolved?

Example

I have a long running calculation and i request the result via $http. Some actions or events require me to restart the calculation (and thus sending a new $http request) before the initial promise is resolved. Thus i imagine i can't use a simple implementation like

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

because I can't ensure that the responses come back in the order in which i did send the requests - after all i want to show the result of the latest calculation when all promises were resolved properly.

However I would like to avoid waiting for the first response until i send a new request like this:

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 config-object on the response to use some timestamps or other identifiers to manually order the incoming responses. However i was hoping I could just tell angular somehow to cancel an outdated promise and thus not run the .then() function when it gets resolved.

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

Maybe just rejecting the promise right away is the way to go? But in both cases it might take forever until finally a promise is resolved before the next request is generated (which leads to an empty view in the meantime).

Is there some angular API-Function that i am missing or are there robust design patterns or "tricks" with promise chaining or $q.all to handle multiple promises that return the "same" data?

like image 569
H W Avatar asked Jul 08 '16 11:07

H W


People also ask

What are promises in AngularJS?

Promises in AngularJS are provided by the built-in $q service. They provide a way to execute asynchronous functions in series by registering them with a promise object. {info} Promises have made their way into native JavaScript as part of the ES6 specification.

What is deferred and Promise in AngularJS?

defer() to create a Promise. A Promise is a function that returns a single value or error in the future. So whenever you have some asynchronous process that should return a value or an error, you can use $q. defer() to create a new Promise.

What is $q in AngularJS?

$q is integrated with the $rootScope. Scope Scope model observation mechanism in AngularJS, which means faster propagation of resolution or rejection into your models and avoiding unnecessary browser repaints, which would result in flickering UI. Q has many more features than $q, but that comes at a cost of bytes.

Is Promise synchronous or asynchronous in angular?

Promises in Angular provide an easy way to execute asynchronous functions that use callbacks, while emitting and completing (resolving or rejecting) one value at a time.


2 Answers

I do it by generating a requestId, and in the promise's then() function I check if the response is coming from the most recent requestId.

While this approach does not actually cancel the previous promises, it does provide a quick and easy way to ensure that you are handling the most recent request's response.

Something like:

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 a side-note, I wanted to add that this concept of tracking requestId's can also be applied to preventing duplicate requests. For example, in my Data service's load(module, id) method, I do a little process like this:

  1. generate the requestId based on the URL + parameters.
  2. check in requests hash-table for the requestId

    • if requestId is not found: generate new request and store promise in hash-table
    • if requestId is found: simply return the promise from the hash-table
  3. When the request finishes, remove the requestId's entry from the hash-table.

like image 161
plong0 Avatar answered Sep 22 '22 17:09

plong0


Cancelling a promise is just making it not invoke the onFulfilled and onRejected functions at the then stage. So as @user2263572 mentioned it's always best to let go the promise not cancelled (ES6 native promises can not be cancelled anyways) and handle this condition within it's then stage (like disregarding the task if a global variable is set to 2 as shown in the following snippet) and i am sure you can find tons of other ways to do it. One example could be;

Sorry that i use v (looks like check character) for resolve and x (obvious) 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));
like image 43
Redu Avatar answered Sep 19 '22 17:09

Redu