Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Commit

Permalink
feat($q): $q.all() now accepts hash
Browse files Browse the repository at this point in the history
When waiting for several promises at once, it is often desirable to
have them by name, not just by index in array.

Example of this kind of interface already implemented would be a
$routeProvider.when(url, {resolve: <hash of promises>}), where
resources/promises are given by names, and then results accessed
by names in controller.
  • Loading branch information
ashtuchkin authored and IgorMinar committed Feb 25, 2013
1 parent 7b236b2 commit e27bb6e
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 18 deletions.
35 changes: 18 additions & 17 deletions src/ng/q.js
Original file line number Diff line number Diff line change
Expand Up @@ -377,29 +377,30 @@ function qFactory(nextTick, exceptionHandler) {
* Combines multiple promises into a single promise that is resolved when all of the input
* promises are resolved.
*
* @param {Array.<Promise>} promises An array of promises.
* @returns {Promise} Returns a single promise that will be resolved with an array of values,
* each value corresponding to the promise at the same index in the `promises` array. If any of
* @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
* @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
* each value corresponding to the promise at the same index/key in the `promises` array/hash. If any of
* the promises is resolved with a rejection, this resulting promise will be resolved with the
* same rejection.
*/
function all(promises) {
var deferred = defer(),
counter = promises.length,
results = [];

if (counter) {
forEach(promises, function(promise, index) {
ref(promise).then(function(value) {
if (index in results) return;
results[index] = value;
if (!(--counter)) deferred.resolve(results);
}, function(reason) {
if (index in results) return;
deferred.reject(reason);
});
counter = 0,
results = isArray(promises) ? [] : {};

forEach(promises, function(promise, key) {
counter++;
ref(promise).then(function(value) {
if (results.hasOwnProperty(key)) return;
results[key] = value;
if (!(--counter)) deferred.resolve(results);
}, function(reason) {
if (results.hasOwnProperty(key)) return;
deferred.reject(reason);
});
} else {
});

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

Expand Down
68 changes: 67 additions & 1 deletion test/ng/qSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ describe('q', function() {
});


describe('all', function() {
describe('all (array)', function() {
it('should resolve all of nothing', function() {
var result;
q.all([]).then(function(r) { result = r; });
Expand Down Expand Up @@ -742,6 +742,72 @@ describe('q', function() {
});
});

describe('all (hash)', function() {
it('should resolve all of nothing', function() {
var result;
q.all({}).then(function(r) { result = r; });
mockNextTick.flush();
expect(result).toEqual({});
});


it('should take a hash of promises and return a promise for a hash of results', function() {
var deferred1 = defer(),
deferred2 = defer();

q.all({en: promise, fr: deferred1.promise, es: deferred2.promise}).then(success(), error());
expect(logStr()).toBe('');
syncResolve(deferred, 'hi');
expect(logStr()).toBe('');
syncResolve(deferred2, 'hola');
expect(logStr()).toBe('');
syncResolve(deferred1, 'salut');
expect(logStr()).toBe('success({en:hi,es:hola,fr:salut})');
});


it('should reject the derived promise if at least one of the promises in the hash is rejected',
function() {
var deferred1 = defer(),
deferred2 = defer();

q.all({en: promise, fr: deferred1.promise, es: deferred2.promise}).then(success(), error());
expect(logStr()).toBe('');
syncResolve(deferred2, 'hola');
expect(logStr()).toBe('');
syncReject(deferred1, 'oops');
expect(logStr()).toBe('error(oops)');
});


it('should ignore multiple resolutions of an (evil) hash promise', function() {
var evilPromise = {
then: function(success, error) {
evilPromise.success = success;
evilPromise.error = error;
}
}

q.all({good: promise, evil: evilPromise}).then(success(), error());
expect(logStr()).toBe('');

evilPromise.success('first');
evilPromise.success('muhaha');
evilPromise.error('arghhh');
expect(logStr()).toBe('');

syncResolve(deferred, 'done');
expect(logStr()).toBe('success({evil:first,good:done})');
});

it('should handle correctly situation when given the same promise several times', function() {
q.all({first: promise, second: promise, third: promise}).then(success(), error());
expect(logStr()).toBe('');

syncResolve(deferred, 'done');
expect(logStr()).toBe('success({first:done,second:done,third:done})');
});
});

describe('exception logging', function() {
var mockExceptionLogger = {
Expand Down

0 comments on commit e27bb6e

Please sign in to comment.