Kabachok
Kabachok

Reputation: 603

$q.all of AngularJS only works for one result

I am working on function that calculates a distance from a given object to a number of other places with the help of Google Distance Matrix which is of course async therefore I'm dealing with promises.

When the number of places is one, everything works great. But once I have more than one promise, $q.all doesn't do anything : it neither resolves in a success nor in error. Though I have checked in the console that the calls to the Google distance matrix do happen and return a correct result. Any clue what can be at play here?

I am using AngularJS 1.6.4. Let me know should you need any more details. Thanks!

var requests = [];
for (var i = 0; i < ctrl.places.length; i += 1) {
    var deferred = $q.defer();
    requests.push(deferred.promise);
    var destination = ctrl.places[i].latLng;
    service.getDistanceMatrix({
        origins: [ctrl.origin],
        destinations: [destination[0] + "," + destination[1]],
        travelMode: 'DRIVING'
    }, function(response, status) {
        if (status === 'OK') {
            deferred.resolve(response.rows[0].elements[0].distance.text);
        }
    });
}

$q.all(requests).then(function(result) {
    ctrl.distances = result;
});

Upvotes: 2

Views: 624

Answers (2)

scarlz
scarlz

Reputation: 2512

Your problem is that var is not block-scoped, so the value of deferred will always belong to the final iteration of your loop by the time any of your callbacks are invoked. A consequence of this will that the earlier deferred will never be resolved and the $q.all will appear to hang.

The simplest way to resolve this is to change your use of var to let to take advantage of block scoping:

let deferred = $q.defer();

Upvotes: 2

CodeWarrior
CodeWarrior

Reputation: 2851

Reason why it does not work

By the time the service call resolves and the callback handler is invoked, the deferred attribute is referring to the very last deferred object created by the for loop. So, in effect, you are always performing a resolve on the very last deferred object that was created.

Solution:

Create a new function:

function getDistanceMatrixForDestination (destination, origins) {
   var deferred = $q.defer();
   service.getDistanceMatrix({
        origins: [origins],
        destinations: [destination[0] + "," + destination[1]],
        travelMode: 'DRIVING'
    }, function(response, status) {
        if (status === 'OK') {
            deferred.resolve(response.rows[0].elements[0].distance.text);
        } else {
            deferred.reject();
        }
    });
   return deferred.promise;
}

Change your existing code to this:

var requests = [];
for (var i = 0; i < ctrl.places.length; i += 1) {
    var destination = ctrl.places[i].latLng;
    requests.push(getDistanceMatrixForDestination (destination, ctrl.origins));
}

$q.all(requests).then(function(result) {
    ctrl.distances = result;
});

Upvotes: 1

Related Questions