$q.allSettled – Q.allSettled for angular promises

Q has Q.allSettled. $q does not. The main difference between $q.all and $q.allSettled is that $q.allSettled waits for all promises to resolve or reject (i.e. “settle”). Then it resolves. $q.all rejects when the first project is rejected.

Paraphrased from Q’s documentation:

$q.allSettled returns a promise that is fulfilled with an array of promise state snapshots, but only after all the original promises have settled, i.e. become either fulfilled or rejected.

This method is often used in order to execute a number of operations concurrently and be notified when they all finish, regardless of success or failure.

I’ve noticed a lot of Angular devs request $q.allSettled. So here it is! JS Fiddle.

angular.module("qImproved", [])
.config(function ($provide) {
  $provide.decorator("$q", function ($delegate) {

    /**
     * $q.allSettled returns a promise that is fulfilled with an array of promise state snapshots, 
     * but only after all the original promises have settled, i.e. become either fulfilled or rejected.
     * 
     * This method is often used in order to execute a number of operations concurrently and be 
     * notified when they all finish, regardless of success or failure.
     *
     * @param promises array or object of promises
     * @returns {Promise} when resolved will contain an an array or object of resolved or rejected promises.
     * A resolved promise have the form { status: "fulfilled", value: value }.  A rejected promise will have
     * the form { status: "rejected", reason: reason }.
     */
    function allSettled(promises) {
      var deferred = $delegate.defer(),
          counter = 0,
          results = angular.isArray(promises) ? [] : {};

      angular.forEach(promises, function(promise, key) {
        counter++;
        $delegate.when(promise).then(function(value) {
          if (results.hasOwnProperty(key)) return;
          results[key] = { status: "fulfilled", value: value };
          if (!(--counter)) deferred.resolve(results);
        }, function(reason) {
          if (results.hasOwnProperty(key)) return;
          results[key] = { status: "rejected", reason: reason };
          if (!(--counter)) deferred.resolve(results);
        });
      });

      if (counter === 0) {
        deferred.resolve(results);
      }

      return deferred.promise;
    }
    $delegate.allSettled = allSettled;
    return $delegate;
  });
});

Example usage:

$q.allSettled([promise0, promise1])
.then(function (data) {
  var result0 = data[0];
  var result1 = data[1];
  if (result0.status === "fulfilled") { console.log(result0.value); }
  if (result1.status === "rejected") { console.log(result1.reason); }
});
Steven Wexler
Follow me!

Steven Wexler

Software Engineer at Hurdlr
I'm a software engineer living in Washington D.C. and working at a startup called Hurdlr. I primarily develop in C#, Javascript, and SQL. And I play around with Python and Scala on the side. I enjoy participating as an active member on StackOverflow and working on side projects.Check out my exceptions.js framework!
Steven Wexler
Follow me!

3 thoughts on “$q.allSettled – Q.allSettled for angular promises

Leave a Reply