Object of promises in Angular

$q.all(Array) and $q.all(Object) example

Angularjs provides convenient method for waiting on multiple promises to be resolved in parallel. Usually, I place all promises into an array, then wait on the array using $q.all method. This method can also wait on an object with promises as values. Let us try this under Node

Install and load angular

Latest versions of Angular library are available through NPM registry

npm info angular
npm install angular

Angular NPM installation comes with jsdom, and thus can be loaded directly from Node environment

1
2
3
4
5
6
7
8
9
var angular = require('angular');
console.log(angular.version);
// output
$ node index.js
{ full: '1.2.23',
major: 1,
minor: 2,
dot: 23,
codeName: 'superficial-malady' }

We can grab the $q service directly without creating an application

1
2
3
4
5
6
7
var $q = angular.injector(['ng']).get('$q');
console.log($q);
// output
{ defer: [Function],
reject: [Function],
when: [Function],
all: [Function: all] }

$q.all(Array)

I typically create an array of promises by mapping values. The resolved argument is an array with single result per promise, in the same order as original.

1
2
3
4
5
6
7
8
9
10
var numbers = [100, 20, 42];
function resolveNumber(n) {
return $q.when(n);
}
var promises = numbers.map(resolveNumber);
$q.all(promises).then(function (results) {
console.log('array promises resolved with', results);
});
// output
array promises resolved with [ 100, 20, 42 ]

We can mix promises and values in the array and the resolved array has expected order

1
2
3
4
5
6
7
8
9
10
var promises = [
resolveNumber(42),
-2,
resolveNumber(100)
];
$q.all(promises).then(function (results) {
console.log('mixed array promises resolved with', results);
});
// output
mixed array promises resolved with [ 42, -2, 100 ]

$q.all(Object)

Instead of waiting on an array, we can place promises into an object as values.

1
2
3
4
5
6
7
8
9
10
var obj = {
foo: resolveNumber(42),
bar: 'bar',
baz: resolveNumber(100)
};
console.log(obj);
// output
{ foo: { then: [Function], catch: [Function], finally: [Function] },
bar: 'bar',
baz: { then: [Function], catch: [Function], finally: [Function] } }

We can even mix promises and other value types in the same object. The promise returned by $q.all(obj) will be resolved with another object, each promise value will be replaced with actual value, and non-promise values will remain unchanged

1
2
3
4
5
$q.all(obj).then(function (results) {
console.log('object promises resolved with', results);
});
// output
object promises resolved with { bar: 'bar', foo: 42, baz: 100 }

Notice that the order of properties in the resolved object is different from original object. I think this is due to Angular copying non-promise values first.