From e3c31b9b6a33d8785dc306d4a268fad164c1cdde Mon Sep 17 00:00:00 2001 From: Justin Pomeroy Date: Wed, 17 Feb 2016 15:14:49 -0600 Subject: [PATCH] Maintain order when resolving promise list This updates the $qExtensions.allSettled method so that it maintains the order of the original list of promises. The list of passed and failed promises will be in the same order as they were in the original list. Closes-Bug: #1546758 Change-Id: I9de0b68a16c4f3e2a9a34fb8862de2d77b4a19bb --- .../static/framework/util/q/q.extensions.js | 18 +++++--- .../framework/util/q/q.extensions.spec.js | 45 +++++++++++++++++++ 2 files changed, 57 insertions(+), 6 deletions(-) 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() {