diff --git a/src/utils.js b/src/utils.js index c4d2cb8f8..46a71884f 100644 --- a/src/utils.js +++ b/src/utils.js @@ -4,7 +4,6 @@ import has from 'lodash.has' import humanFormat from 'human-format' import isArray from 'lodash.isarray' import isString from 'lodash.isstring' -import mapToArray from 'lodash.map' import multiKeyHashInt from 'multikey-hash' import xml2js from 'xml2js' import {promisify} from 'bluebird' @@ -107,31 +106,45 @@ export function pFinally (cb) { ) } -// Given an array which contains promises return a promise that is -// fulfilled when all the items in the array are either fulfilled or -// rejected. +// Given a collection (array or object) which contains promises, +// return a promise that is fulfilled when all the items in the +// collection are either fulfilled or rejected. +// +// This promise will be fulfilled with a collection (of the same type, +// array or object) containing promise inspections. export function pSettle (promises) { - return Promise.all(mapToArray( - promises, - promise => Promise.resolve(promise).then( - value => ({ - isFulfilled: () => true, - isRejected: () => false, - value: () => value, - reason: () => { - throw new Error('no reason, the promise has been fulfilled') + let mainPromise = Promise.resolve() + + const results = 'length' in promises + ? new Array(promises.length) + : {} + + forEach(promises, (promise, key) => { + mainPromise = mainPromise.then(() => promise).then( + value => { + results[key] = { + isFulfilled: () => true, + isRejected: () => false, + value: () => value, + reason: () => { + throw new Error('no reason, the promise has been fulfilled') + } } - }), - reason => ({ - isFulfilled: () => false, - isRejected: () => true, - value: () => { - throw new Error('no value, the promise has been rejected') - }, - reason: () => reason - }) + }, + reason => { + results[key] = { + isFulfilled: () => false, + isRejected: () => true, + value: () => { + throw new Error('no value, the promise has been rejected') + }, + reason: () => reason + } + } ) - )) + }) + + return mainPromise.then(() => results) } // ------------------------------------------------------------------- diff --git a/src/utils.spec.js b/src/utils.spec.js index 0fbd66fdd..be42065bc 100644 --- a/src/utils.spec.js +++ b/src/utils.spec.js @@ -122,38 +122,6 @@ describe('generateToken()', () => { // ------------------------------------------------------------------- -describe('pSettle()', () => { - it('makes an array of PromiseInspection', async () => { - const [ - status1, - status2, - status3 - ] = await pSettle([ - Promise.resolve(42), - Math.PI, - Promise.reject('fatality') - ]) - - expect(status1.isRejected()).to.equal(false) - expect(status2.isRejected()).to.equal(false) - expect(status3.isRejected()).to.equal(true) - - expect(status1.isFulfilled()).to.equal(true) - expect(status2.isFulfilled()).to.equal(true) - expect(status3.isFulfilled()).to.equal(false) - - expect(status1.value()).to.equal(42) - expect(status2.value()).to.equal(Math.PI) - expect(::status3.value).to.throw() - - expect(::status1.reason).to.throw() - expect(::status2.reason).to.throw() - expect(status3.reason()).to.equal('fatality') - }) -}) - -// ------------------------------------------------------------------- - describe('parseSize()', function () { it('parses a human size', function () { expect(parseSize('1G')).to.equal(1e9) @@ -207,25 +175,59 @@ describe('pFinally()', () => { // ------------------------------------------------------------------- describe('pSettle()', () => { - it('makes an array of PromiseInspection', async () => { + it('works with arrays', async () => { const [ status1, - status2 + status2, + status3 ] = await pSettle([ Promise.resolve(42), + Math.PI, Promise.reject('fatality') ]) expect(status1.isRejected()).to.equal(false) - expect(status2.isRejected()).to.equal(true) + expect(status2.isRejected()).to.equal(false) + expect(status3.isRejected()).to.equal(true) expect(status1.isFulfilled()).to.equal(true) - expect(status2.isFulfilled()).to.equal(false) + expect(status2.isFulfilled()).to.equal(true) + expect(status3.isFulfilled()).to.equal(false) expect(status1.value()).to.equal(42) - expect(::status2.value).to.throw() + expect(status2.value()).to.equal(Math.PI) + expect(::status3.value).to.throw() expect(::status1.reason).to.throw() - expect(status2.reason()).to.equal('fatality') + expect(::status2.reason).to.throw() + expect(status3.reason()).to.equal('fatality') + }) + + it('works with objects', async () => { + const { + a: status1, + b: status2, + c: status3 + } = await pSettle({ + a: Promise.resolve(42), + b: Math.PI, + c: Promise.reject('fatality') + }) + + expect(status1.isRejected()).to.equal(false) + expect(status2.isRejected()).to.equal(false) + expect(status3.isRejected()).to.equal(true) + + expect(status1.isFulfilled()).to.equal(true) + expect(status2.isFulfilled()).to.equal(true) + expect(status3.isFulfilled()).to.equal(false) + + expect(status1.value()).to.equal(42) + expect(status2.value()).to.equal(Math.PI) + expect(::status3.value).to.throw() + + expect(::status1.reason).to.throw() + expect(::status2.reason).to.throw() + expect(status3.reason()).to.equal('fatality') }) })