diff --git a/horizon/static/framework/util/q/q.extensions.js b/horizon/static/framework/util/q/q.extensions.js index d694bb9a78..b518ce55a5 100644 --- a/horizon/static/framework/util/q/q.extensions.js +++ b/horizon/static/framework/util/q/q.extensions.js @@ -62,8 +62,8 @@ * promises if any promise is rejected, this will wait for all promises * to settle. * - * The order of the resolve or rejection reasons is non-deterministic - * and should not be relied upon for correlation to input promises. + * The order of the resolve or rejection reasons correlates directly to + * the order of the promises in the list. * * @param {array} promiseList * The list of promises to resolve @@ -100,18 +100,18 @@ $q.all(promises).then(onComplete); return deferred.promise; - function resolveSingle(singlePromise) { + function resolveSingle(singlePromise, index) { var deferredInner = $q.defer(); singlePromise.promise.then(onResolve, onReject); return deferredInner.promise; function onResolve(response) { - passList.push(formatResponse(response, singlePromise.context)); + passList[index] = formatResponse(response, singlePromise.context); deferredInner.resolve(); } function onReject(response) { - failList.push(formatResponse(response, singlePromise.context)); + failList[index] = formatResponse(response, singlePromise.context); deferredInner.resolve(); } @@ -124,7 +124,13 @@ } function onComplete() { - deferred.resolve({pass: passList, fail: failList}); + deferred.resolve({pass: condense(passList), fail: condense(failList)}); + } + + function condense(promiseList) { + return promiseList.filter(function removeEmpty(promise) { + return !!promise; + }); } } diff --git a/horizon/static/framework/util/q/q.extensions.spec.js b/horizon/static/framework/util/q/q.extensions.spec.js index 2d75118089..8177372f84 100644 --- a/horizon/static/framework/util/q/q.extensions.spec.js +++ b/horizon/static/framework/util/q/q.extensions.spec.js @@ -60,6 +60,51 @@ expect(resolvedPromises.pass[0]).toEqual({data: 'passed', context: '2'}); } }); + + it('should maintain order of promises regardless of resolve/reject order', function() { + var defs = [$q.defer(), $q.defer(), $q.defer(), $q.defer(), $q.defer(), $q.defer()]; + service.allSettled([{ + promise: defs[0].promise, + context: '1' + },{ + promise: defs[1].promise, + context: '2' + },{ + promise: defs[2].promise, + context: '3' + },{ + promise: defs[3].promise, + context: '4' + },{ + promise: defs[4].promise, + context: '5' + },{ + promise: defs[5].promise, + context: '6' + }]).then(onAllSettled); + + defs[1].reject(); + defs[2].resolve(); + defs[0].resolve(); + defs[5].reject(); + defs[3].reject(); + defs[4].resolve(); + + $scope.$apply(); + + function onAllSettled(resolvedPromises) { + var pass = resolvedPromises.pass; + var fail = resolvedPromises.fail; + expect(pass.length).toBe(3); + expect(fail.length).toBe(3); + expect(pass[0].context).toBe('1'); + expect(pass[1].context).toBe('3'); + expect(pass[2].context).toBe('5'); + expect(fail[0].context).toBe('2'); + expect(fail[1].context).toBe('4'); + expect(fail[2].context).toBe('6'); + } + }); }); describe('booleanAsPromise', function() {