Select: Make compose-friendly by taking a known number of arguments.

This commit is contained in:
David Braun 2017-10-17 09:54:34 -04:00
parent d7355e17e1
commit 6ea2b8bafe
5 changed files with 24 additions and 22 deletions

View file

@ -4,7 +4,7 @@
- [close() -> async](#close---async) - [close() -> async](#close---async)
- [readOnly() -> Channel](#readonly---channel) - [readOnly() -> Channel](#readonly---channel)
- [writeOnly() -> Channel](#writeonly---channel) - [writeOnly() -> Channel](#writeonly---channel)
- [Channel.select(methods) -> async channel](#channelselectmethods---async-channel) - [Channel.select(promises) -> async channel](#channelselectpromises---async-channel)
- [Examples](#examples) - [Examples](#examples)
- [value](#value) - [value](#value)
- [Array-like Properties](#array-like-properties) - [Array-like Properties](#array-like-properties)
@ -47,14 +47,13 @@ Return a version of the channel that provides only read methods.
Return a version of the channel that provides only write methods. Return a version of the channel that provides only write methods.
## Channel.select(methods) -> async channel ## Channel.select(promises) -> async channel
Attempt to call multiple channel `methods` in parallel and return the channel of Wait for the first channel method promise to succeed and then cancel the rest.
the first one that succeeds. Only the winning method is executed to Return the channel of the winning promise.
completion—the other methods have no effect.
All of the methods can be cancelled before completion by calling `cancel` on the All of the promises can be cancelled before completion by calling `cancel` on
promise returned from `select`. the promise returned by `select`.
### Examples ### Examples
@ -62,11 +61,11 @@ Imagine you're at a party and your next conversation depends on whom you run
into first: Alice, Bob, or Charlie. into first: Alice, Bob, or Charlie.
```JavaScript ```JavaScript
switch (await Channel.select( switch (await Channel.select([
alice.shift(), alice.shift(),
bob.shift(), bob.shift(),
charlie.push(`Hi!`) charlie.push(`Hi!`)
)) { ])) {
case alice: case alice:
console.log(`Alice said ${alice.value}.`); console.log(`Alice said ${alice.value}.`);
break; break;
@ -92,7 +91,7 @@ const increment = () => {
return counter; return counter;
}; };
await Channel.select(alice.push(increment()), bob.push(increment())); await Channel.select([alice.push(increment()), bob.push(increment())]);
assert.equal(counter, 2); assert.equal(counter, 2);
``` ```
@ -103,7 +102,7 @@ channel to return immediately even if no other channels are ready:
const closed = Channel(); const closed = Channel();
closed.close(); closed.close();
switch (await Channel.select(alice.shift(), bob.shift(), closed.shift())) { switch (await Channel.select([alice.shift(), bob.shift(), closed.shift())]) {
case alice: case alice:
console.log(`Alice said ${alice.value}.`); console.log(`Alice said ${alice.value}.`);
break; break;
@ -123,7 +122,7 @@ You can also arrange it so that the `select` completes within a timeout:
const timeout = Channel(); const timeout = Channel();
setTimeout(timeout.close, 1000); setTimeout(timeout.close, 1000);
switch (await Channel.select(alice.shift(), bob.shift(), timeout.shift())) { switch (await Channel.select([alice.shift(), bob.shift(), timeout.shift())]) {
case alice: case alice:
console.log(`Alice said ${alice.value}.`); console.log(`Alice said ${alice.value}.`);
break; break;

View file

@ -300,7 +300,7 @@ Channel.of = (...values) => Channel.from(values);
Channel.isChannel = arg => prototype.isPrototypeOf(arg); Channel.isChannel = arg => prototype.isPrototypeOf(arg);
Channel.select = (...methodPromises) => Channel.select = methodPromises =>
Object.assign( Object.assign(
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
methodPromises.forEach(async promise => { methodPromises.forEach(async promise => {

View file

@ -18,7 +18,7 @@ it(`doubleselect`, async function() {
// different channels. // different channels.
const sender = async (n, c1, c2, c3, c4) => { const sender = async (n, c1, c2, c3, c4) => {
for (let i = 0; i < n; i++) { for (let i = 0; i < n; i++) {
await Channel.select(c1.push(i), c2.push(i), c3.push(i), c4.push(i)); await Channel.select([c1.push(i), c2.push(i), c3.push(i), c4.push(i)]);
} }
c1.close(); c1.close();

View file

@ -24,11 +24,11 @@ it(`select`, async function() {
let i = 0; let i = 0;
do { do {
switch (await Channel.select( switch (await Channel.select([
a.push(GetValue()), a.push(GetValue()),
b.push(GetValue()), b.push(GetValue()),
closed.shift() closed.shift()
)) { ])) {
case a: case a:
i++; i++;
a = Channel(); a = Channel();

View file

@ -89,10 +89,10 @@ describe(`Channel`, function() {
await a.shift(); await a.shift();
})(); })();
assert.equal(await Channel.select(a.shift(), b.shift()), b); assert.equal(await Channel.select([a.shift(), b.shift()]), b);
assert.equal(b.value, 0); assert.equal(b.value, 0);
assert.equal(await a.shift(), 1); assert.equal(await a.shift(), 1);
assert.equal(await Channel.select(a.push(0), b.shift()), a); assert.equal(await Channel.select([a.push(0), b.shift()]), a);
}); });
}); });
@ -102,7 +102,7 @@ describe(`Channel`, function() {
const nonBlocking = Channel(); const nonBlocking = Channel();
nonBlocking.close(); nonBlocking.close();
switch (await Channel.select(a.shift(), b.push(0), nonBlocking.shift())) { switch (await Channel.select([a.shift(), b.push(0), nonBlocking.shift()])) {
case a: case a:
assert(false); assert(false);
break; break;
@ -119,9 +119,12 @@ describe(`Channel`, function() {
it(`cancel`, async function() { it(`cancel`, async function() {
const channel = Channel(); const channel = Channel();
Channel.select(channel.push(`cancelled`)).cancel(); Channel.select([channel.push(`cancelled`)]).cancel();
const closed = Channel.of(); const closed = Channel.of();
assert.equal(await Channel.select(channel.shift(), closed.shift()), closed); assert.equal(
await Channel.select([channel.shift(), closed.shift()]),
closed
);
}); });
}); });
@ -251,7 +254,7 @@ describe(`Channel object`, function() {
const channel = Channel(); const channel = Channel();
return assertRejects(async () => { return assertRejects(async () => {
await Channel.select(channel.push(undefined)); await Channel.select([channel.push(undefined)]);
}, new TypeError(`Can't push 'undefined' to channel, use close instead.`)); }, new TypeError(`Can't push 'undefined' to channel, use close instead.`));
}); });
}); });