aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/ng/q.js35
-rw-r--r--test/ng/qSpec.js68
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 = {