diff options
| -rw-r--r-- | src/ng/q.js | 35 | ||||
| -rw-r--r-- | test/ng/qSpec.js | 68 |
2 files changed, 85 insertions, 18 deletions
diff --git a/src/ng/q.js b/src/ng/q.js index f3bd1d6e..6cb92102 100644 --- a/src/ng/q.js +++ b/src/ng/q.js @@ -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); } diff --git a/test/ng/qSpec.js b/test/ng/qSpec.js index 941b4f2e..822cde6b 100644 --- a/test/ng/qSpec.js +++ b/test/ng/qSpec.js @@ -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; }); @@ -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 = { |
