From 67012b6f4b3f9b0090ea544ae431086de52a741e Mon Sep 17 00:00:00 2001 From: David Braun Date: Sun, 15 Oct 2017 13:32:40 -0400 Subject: [PATCH] select: Make promise cancellable. --- README.md | 3 +++ lib/index.js | 49 ++++++++++++++++++++++++++----------------------- test/index.js | 7 +++++++ 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index b760142..0f1c7f4 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,9 @@ Returns a version of the channel that provides only write methods. returns the channel of the first one that succeeds. Only the winning method is executed to completion—the other methods have no effect. +All of the methods can be cancelled before completion by calling `cancel` on the +promise returned from `select`. + #### Examples Imagine you're at a party and your next conversation depends on whom you run diff --git a/lib/index.js b/lib/index.js index 0cd785a..4ddbb33 100644 --- a/lib/index.js +++ b/lib/index.js @@ -309,31 +309,34 @@ Channel.of = (...values) => Channel.isChannel = (arg) => prototype.isPrototypeOf(arg) -Channel.select = (...methodPromises) => { - const promise = new Promise((resolve, reject) => { - methodPromises.forEach(async (promise) => { - promise.prethen(() => { - // We've been given a heads-up that this method will complete first so - // cancel the other method calls. - methodPromises.forEach((other) => { - if (other !== promise) { - other.cancel() - } +Channel.select = (...methodPromises) => + Object.assign( + new Promise((resolve, reject) => { + methodPromises.forEach(async (promise) => { + promise.prethen(() => { + // We've been given a heads-up that this method will complete first so + // cancel the other method calls. + methodPromises.forEach((other) => { + if (other !== promise) { + other.cancel() + } + }) }) + + try { + await promise + } catch (exception) { + reject(exception) + } + + resolve(promise.channel) }) - - try { - await promise - } catch (exception) { - reject(exception) - } - - resolve(promise.channel) - }) - }) - - return promise -} + }), + { + cancel: () => + Promise.all(methodPromises.map((promise) => promise.cancel())) + } + ) // functional interface allowing full or partial application // diff --git a/test/index.js b/test/index.js index 59be1b2..09e9450 100644 --- a/test/index.js +++ b/test/index.js @@ -118,6 +118,13 @@ describe(`Channel`, function () { break } }) + + it(`cancel`, async function () { + const channel = Channel() + Channel.select(channel.push(`cancelled`)).cancel() + const closed = Channel.of() + assert.equal(await Channel.select(channel.shift(), closed.shift()), closed) + }) }) describe(`functional interface`, async function () {