diff --git a/q.js b/q.js index 374ca851..cb1e000c 100644 --- a/q.js +++ b/q.js @@ -640,7 +640,7 @@ array_reduce( [ "isFulfilled", "isRejected", "isPending", "dispatch", - "when", "spread", + "when", "spread", "spreadResolved", "get", "set", "del", "delete", "post", "send", "invoke", "keys", @@ -1061,6 +1061,29 @@ function spread(promise, fulfilled, rejected) { }, rejected); } +/** + * Spreads the values or rejection reasons of a promised array of arguments + * into the single resolved callback. + * @param resolved callback that receives variadic arguments from the + * promised array, either fulfilled values or rejection reasons + * @returns a promise for the return value. + */ +Q.spreadResolved = spreadResolved; +function spreadResolved (promise, resolved) { + return when(promise, function (valuesOrPromises) { + return allResolved(valuesOrPromises).then(function (promises) { + return resolved.apply(void 0, array_map(promises, function (promise) { + if (promise.isFulfilled()) { + return promise.valueOf(); + } + else { + return promise.valueOf().exception; + } + })); + }); + }); +} + /** * The async function is a decorator for generator functions, turning * them into asynchronous generators. Although generators are only part diff --git a/spec/q-spec.js b/spec/q-spec.js index 15643f0f..cf14338e 100644 --- a/spec/q-spec.js +++ b/spec/q-spec.js @@ -1236,6 +1236,60 @@ describe("spread", function () { }); +describe("spreadResolved", function () { + + it("spreads values across arguments", function () { + return Q.spreadResolved([1, 2, 3], function (a, b) { + expect(b).toBe(2); + }); + }); + + it("spreads promises for arrays across arguments", function () { + return Q.resolve([Q.resolve(10)]) + .spreadResolved(function (value) { + expect(value).toEqual(10); + }); + }); + + it("spreads arrays of fulfilled promises across arguments", function () { + var deferredA = Q.defer(); + var deferredB = Q.defer(); + + var promise = Q.spreadResolved([deferredA.promise, deferredB.promise], + function (a, b) { + expect(a).toEqual(10); + expect(b).toEqual(20); + }); + + Q.delay(5).then(function () { + deferredA.resolve(10); + }); + Q.delay(10).then(function () { + deferredB.resolve(20); + }); + + return promise; + }); + + it("spreads arrays of fulfilled and rejected promises across arguments", function () { + var err1 = new Error('err1'); + var err2 = new Error('err2'); + return Q.spreadResolved([3, Q.resolve(10), Q.reject(err1), Q.resolve(20), Q.reject(err2), 30], + function (a, b, c, d, e, f) { + expect(a).toBe(3); + expect(b).toBe(10); + expect(c).toBe(err1) + expect(c.message).toBe('err1'); + expect(d).toBe(20); + expect(e).toBe(err2); + expect(e.message).toBe('err2'); + expect(f).toBe(30); + } + ); + }); + +}); + describe("fin", function () { var exception1 = new Error("boo!");