Wild Widow
Wild Widow

Reputation: 2549

how to continue with $q.all() when some promises fail

I have an array of promises and each promise calls http.get().

var items = ["URL1", "URL2", "URL3"];
var promises = [];
//on each URL in items array, I want to create a promise and call http.get
items.forEach(function(el){
    return promises.push($http.get(el)); 
});
var all = $q.all(promises);
all.then(function success(data){
    console.log(data);
}).catch(function(reason){
    console.log("reason is", reason);
});

what's happening in my case

URL2.get does not get resolved, and it immediately triggers the catch() in the $q.all. As a result of this failure all.then() never gets invoked.

what I want

I want all the promises to continue even if one of the promise is rejected.

I found a similar post but the solution suggests to use another angular pacakage called Kris Kowal's Q. so I'm wondering how can I achieve it without using external package?

Upvotes: 4

Views: 5867

Answers (3)

una
una

Reputation: 21

i wrapped the $resource in $q with only resolve state

var promises = [
     $q(function (resolve) {
        Me.get({params: 12}).$promise.then(function (data) {
           resolve(data);
        }, function (err) {
           resolve(err);
        });
     }),
     $q(function (resolve) {
        Me.get({params: 123}).$promise.then(function (data) {
           resolve(data);
        }, function (err) {
           resolve(err);
        });
     }),
     $q(function (resolve) {
        Me.get({params: 124}).$promise.then(function (data) {
           resolve(data);
        }, function (err) {
           resolve(err);
        });
     })];

and then use $q.all to promises

Upvotes: 1

jfriend00
jfriend00

Reputation: 707536

Here's an ES6 compatible version of .settle() which allows all promises to finish and you can then query each result to see if it succeeded or failed:

// ES6 version of settle
Promise.settle = function(promises) {
    function PromiseInspection(fulfilled, val) {
        return {
            isFulfilled: function() {
                return fulfilled;
            }, isRejected: function() {
                return !fulfilled;
            }, isPending: function() {
                // PromiseInspection objects created here are never pending
                return false;
            }, value: function() {
                if (!fulfilled) {
                    throw new Error("Can't call .value() on a promise that is not fulfilled");
                }
                return val;
            }, reason: function() {
                if (fulfilled) {
                    throw new Error("Can't call .reason() on a promise that is fulfilled");
                }
                return val;
            }
        };
    }

    return Promise.all(promises.map(function(p) {
        // make sure any values or foreign promises are wrapped in a promise
        return Promise.resolve(p).then(function(val) {
            return new PromiseInspection(true, val);
        }, function(err) {
            return new PromiseInspection(false, err);
        });
    }));
}

This can be adapted for the Q library like this:

// Q version of settle
$q.settle = function(promises) {
    function PromiseInspection(fulfilled, val) {
        return {
            isFulfilled: function() {
                return fulfilled;
            }, isRejected: function() {
                return !fulfilled;
            }, isPending: function() {
                // PromiseInspection objects created here are never pending
                return false;
            }, value: function() {
                if (!fulfilled) {
                    throw new Error("Can't call .value() on a promise that is not fulfilled");
                }
                return val;
            }, reason: function() {
                if (fulfilled) {
                    throw new Error("Can't call .reason() on a promise that is fulfilled");
                }
                return val;
            }
        };
    }

    return $q.all(promises.map(function(p) {
        // make sure any values or foreign promises are wrapped in a promise
        return $q(p).then(function(val) {
            return new PromiseInspection(true, val);
        }, function(err) {
            return new PromiseInspection(false, err);
        });
    }));
}

Usage with your particular code:

var items = ["URL1", "URL2", "URL3"];
$q.settle(items.map(function(url) {
    return $http.get(url);
})).then(function(data){
    data.forEach(function(item) {
       if (item.isFulfilled()) {
           console.log("success: ", item.value());
       } else {
           console.log("fail: ", item.reason());
       }
    });
});

Note: .settle() returns a promise that always resolves, never rejects. This is because no matter how many promises you pass it reject, it still resolves, but returns the info on which promises you passed it resolves or rejected.

Upvotes: 3

mido
mido

Reputation: 25034

a simple hack might be adding a catch block to promises that return null, and filtering the null results from you promise.all result, something like:

let items = ["URL1", "URL2", "URL3"]
  , promises = items.map(url => $http.get(url).catch(e => null))
  , all = $q.all(promises).then(data => data.filter(d => !!d))

all.then(data => {
  // do something with data
}).catch(e => {
  // some error action
})

same thing in ES5:

var  items = ["URL1", "URL2", "URL3"]
  , promises = items.map(function(url){
     return $http.get(url).catch(function(e){return null})
  })
  , all = $q.all(promises).then(function(data){
    return data.filter(function(d){return !!d}) // filter out empty, null results
  })

all.then(function(data){
  // do something with data
}).catch(function(e){
  // some error action
})

Upvotes: 9

Related Questions