diff --git a/packages/microcosm/docs/api/microcosm.md b/packages/microcosm/docs/api/microcosm.md index b0b4a0b6..4bb309a0 100644 --- a/packages/microcosm/docs/api/microcosm.md +++ b/packages/microcosm/docs/api/microcosm.md @@ -333,7 +333,9 @@ fork.push(getPeople) Create a new "group" action bound to the resolution of a list of actions. If all actions resolve or cancel, the group action will -resolve. If any action is rejected, the group action fails: ++resolve. If any action is rejected, the group action fails. If all +actions resolve, their respective payloads will be available in the +`onDone` callback: ```javascript let group = repo.parallel([ @@ -341,8 +343,8 @@ let group = repo.parallel([ repo.push(actionTwo) ]) -group.onDone(function () { - console.log('hurrah!') +group.onDone(function ([answer1, answer2]) { + console.log('hurrah!', answer1, answer2) }) ``` diff --git a/packages/microcosm/src/action.js b/packages/microcosm/src/action.js index cfddfb6d..aad2e017 100644 --- a/packages/microcosm/src/action.js +++ b/packages/microcosm/src/action.js @@ -189,23 +189,27 @@ class Action extends Emitter { */ link(actions: Action[]): this { let outstanding = actions.length + let answers = [] - const onResolve = () => { - if (outstanding <= 1) { - this.resolve() - } else { - outstanding -= 1 - } + if (actions.length === 0) { + return this.resolve(answers) } actions.forEach(action => { + let onResolve = answer => { + answers[actions.indexOf(action)] = answer + outstanding -= 1 + + if (outstanding <= 0) { + this.resolve(answers) + } + } + action.onDone(onResolve) action.onCancel(onResolve) action.onError(this.reject) }) - onResolve() - return this } diff --git a/packages/microcosm/test/unit/action/link.test.js b/packages/microcosm/test/unit/action/link.test.js new file mode 100644 index 00000000..549682a0 --- /dev/null +++ b/packages/microcosm/test/unit/action/link.test.js @@ -0,0 +1,63 @@ +import { Microcosm } from 'microcosm' + +function delay(payload, n) { + return new Promise(resolve => setTimeout(() => resolve(payload), n)) +} + +const cancels = () => { + return action => action.cancel() +} + +const rejects = () => { + return action => action.reject() +} + +describe('Action link', function() { + it('remembers and returns the results of each child action', async function() { + const repo = new Microcosm() + + const action = repo.parallel([ + repo.push(() => delay(1, 20)), + repo.push(() => delay(2, 10)), + repo.push(() => delay(3, 15)) + ]) + + let payload = await action + + expect(payload).toEqual([1, 2, 3]) + }) + + it('resolves empty', async function() { + const repo = new Microcosm() + + const action = repo.parallel([]) + + let payload = await action + + expect(payload).toEqual([]) + }) + + it('handles cancelled actions by just returning undefined in that position', function() { + const repo = new Microcosm() + + const action = repo.parallel([ + repo.push(() => 1), + repo.push(cancels), + repo.push(() => 3) + ]) + + expect(action.payload).toEqual([1, undefined, 3]) + }) + + it('rejects if any of the actions reject', function() { + const repo = new Microcosm() + + const action = repo.parallel([ + repo.push(rejects), + repo.push(() => 2), + repo.push(() => 3) + ]) + + expect(action.status).toEqual('reject') + }) +})