gochan/API.md
2021-02-08 17:04:26 -06:00

405 lines
13 KiB
Markdown

<!-- TOC -->
- [New Properties](#new-properties)
- [close() -> (async)](#close-async)
- [readOnly() -> Channel](#readonly-channel)
- [writeOnly() -> Channel](#writeonly-channel)
- [Channel.all(channels) -> Channel](#channelallchannels-channel)
- [Channel.select(promises) -> (async) channel](#channelselectpromises-async-channel)
- [Examples](#examples)
- [value()](#value)
- [Array-like Properties](#array-like-properties)
- [Channel](#channel)
- [Channel([bufferLength]) -> Channel](#channelbufferlength-channel)
- [Channel.isChannel(value) -> Boolean](#channelischannelvalue-boolean)
- [Channel.of(...values) -> read-only Channel](#channelofvalues-read-only-channel)
- [Channel.from(callback | iterable | stream.Readable[, mapfn [, thisArg]]) -> read-only Channel](#channelfromcallback-iterable-streamreadable-mapfn-thisarg-read-only-channel)
- [Examples](#examples-1)
- [Channel Object](#channel-object)
- [concat(...arguments) -> Channel](#concatarguments-channel)
- [every(callbackfn[, thisArg]) -> (async) Boolean](#everycallbackfn-thisarg-async-boolean)
- [filter(callbackfn[, thisArg]) -> Channel](#filtercallbackfn-thisarg-channel)
- [flat([depth = 1]) -> Channel](#flatdepth-1-channel)
- [flatMap (mapperFunction[, thisArg]) -> Channel](#flatmap-mapperfunction-thisarg-channel)
- [forEach(callbackfn[, thisArg]) -> (async)](#foreachcallbackfn-thisarg-async)
- [join(separator) -> (async) String](#joinseparator-async-string)
- [length](#length)
- [map(mapperFunction[, thisArg]) -> Channel](#mapmapperfunction-thisarg-channel)
- [push(value) -> (async) bufferLength](#pushvalue-async-bufferlength)
- [reduce(callbackfn[, initialValue]) -> (async)](#reducecallbackfn-initialvalue-async)
- [shift() -> (async)](#shift-async)
- [slice(start[, end]) -> Channel](#slicestart-end-channel)
- [some(callbackfn[, thisArg])](#somecallbackfn-thisarg)
- [toString() -> String](#tostring-string)
- [values() -> (async) iterator](#values-async-iterator)
- [Functional API](#functional-api)
<!-- /TOC -->
# New Properties
The following properties don't have equivalents in `Array`.
## close() -> (async)
Close the channel so that no more values can be pushed to it. Return a promise
that resolves when any remaining pushes in flight complete.
Attempting to push to a closed channel will throw an exception. Shifting from
a closed channel will immediately return `undefined`.
## readOnly() -> Channel
Return a version of the channel that provides only read methods.
## writeOnly() -> Channel
Return a version of the channel that provides only write methods.
## Channel.all(channels) -> Channel
Take an array of channels and wait for the next value in each one before pushing
an array of the values to a newly created channel. Similar to `Promise.all`.
## Channel.select(promises) -> (async) channel
Wait for the first channel method promise to succeed and then cancel the rest.
Return the channel of the winning promise.
All of the promises can be cancelled before completion by calling `cancel` on
the promise returned by `select`.
### Examples
Imagine you're at a party and your next conversation depends on whom you run
into first: Alice, Bob, or Charlie.
```JavaScript
switch (await Channel.select([
alice.shift(),
bob.shift(),
charlie.push(`Hi!`)
])) {
case alice:
console.log(`Alice said ${alice.value()}.`);
break;
case bob:
console.log(`Bob said ${bob.value()}.`);
break;
case charlie:
console.log(`I said "hi" to Charlie.`);
break;
}
```
Be careful of unintended side effects, however. Even though only one value is
pushed in the following example, the counter is incremented twice.
```JavaScript
let counter = 0;
const increment = () => {
counter++;
return counter;
};
await Channel.select([alice.push(increment()), bob.push(increment())]);
assert.equal(counter, 2);
```
Sometimes you don't want to wait until a method completes. You can use a closed
channel to return immediately even if no other channels are ready:
```JavaScript
const closed = Channel();
closed.close();
switch (await Channel.select([alice.shift(), bob.shift(), closed.shift())]) {
case alice:
console.log(`Alice said ${alice.value()}.`);
break;
case bob:
console.log(`Bob said ${bob.value()}.`);
break;
default:
console.log(`No one has anything to say yet.`);
}
```
You can also arrange it so that the `select` completes within a timeout:
```JavaScript
const timeout = Channel();
setTimeout(timeout.close, 1000);
switch (await Channel.select([alice.shift(), bob.shift(), timeout.shift())]) {
case alice:
console.log(`Alice said ${alice.value()}.`);
break;
case bob:
console.log(`Bob said ${bob.value()}.`);
break;
default:
console.log(`I stopped listening after one second.`);
}
```
## value()
Return the most recently `shift`ed value. This is useful when used in
combination with `select`.
# Array-like Properties
These properties are similar to the equivalently named properties of `Array`.
## Channel
### Channel([bufferLength]) -> Channel
Create a new `Channel` with an optional buffer. This allows an async function
to push up to `bufferLength` values before blocking.
### Channel.isChannel(value) -> Boolean
Return `true` if `value` is a channel, `false` otherwise.
### Channel.of(...values) -> read-only Channel
Push `values` into a new channel and then close it.
### Channel.from(callback | iterable | stream.Readable[, mapfn [, thisArg]]) -> read-only Channel
Create a new `Channel` from a callback function, an iterable, or a [Node.js
readable stream](https://nodejs.org/api/stream.html#stream_readable_streams).
If given a callback function, call the function repeatedly to obtain values for
pushing into the channel. Close the channel when the function returns
`undefined`.
If the optional `mapfn` argument is provided, call it (using the also optional
`thisArg`) on each value before pushing it into the channel.
#### Examples
```JavaScript
const randomValues = Channel.from(Math.random);
const fromArray = Channel.from([0, 1, 2]);
const generator = function*() {
let counter = 0;
for (;;) {
yield counter;
counter++;
}
};
const fromGenerator = Channel.from(generator());
const fromStream = Channel.from(
fs.createReadStream(`Communicating Sequential Processes.pdf`)
);
```
## Channel Object
### concat(...arguments) -> Channel
When the `concat` method is called with zero or more arguments, it returns a
channel containing the values of the channel followed by the channel values of
each argument in order.
### every(callbackfn[, thisArg]) -> (async) Boolean
`callbackfn` should be a function that accepts one argument and returns a value
that is coercible to the Boolean values `true` or `false`. `every` calls
`callbackfn` once for each value present in the channel until it finds one where
`callbackfn` returns `false`. If such a value is found, every immediately
returns `false`. Otherwise, if `callbackfn` returned `true` for all elements,
`every` will return `true`.
If a `thisArg` parameter is provided, it will be used as the `this` value for
each invocation of `callbackfn`. If it is not provided, `undefined` is used
instead.
Unlike in the Array version of `every`, `callbackfn` is called with only one
argument.
### filter(callbackfn[, thisArg]) -> Channel
`callbackfn` should be a function that accepts an argument and returns a value
that is coercible to the Boolean values `true` or `false`. `filter` calls
`callbackfn` once for each value in the channel and constructs a new channel of
all the values for which `callbackfn` returns true.
If a `thisArg` parameter is provided, it will be used as the `this` value for
each invocation of `callbackfn`. If it is not provided, `undefined` is used
instead.
Unlike in the Array version of `filter`, `callbackfn` is called with only one
argument.
### flat([depth = 1]) -> Channel
Create a new channel with values from the existing channel. If any of the
values are themselves channels, flatten them by pushing their values into the
new channel instead (while repeating this behavior up to `depth` times).
### flatMap (mapperFunction[, thisArg]) -> Channel
Call `mapperFunction` once for each value in the channel and flatten the result
(with depth 1).
If `thisArg` is provided it will be used as the `this` value for each invocation
of `mapperFunction`. If it is not provided, `undefined` is used instead.
Unlike in `Array`'s `flatMap` method, `mapperFunction` is called with only one
argument.
### forEach(callbackfn[, thisArg]) -> (async)
The promise returned by `forEach` resolves when the channel is closed:
```JavaScript
const toArray = async channel => {
const array = [];
await channel.forEach(value => {
array.push(value);
});
return array;
};
```
If `callbackfn` is async then `forEach` will wait for it before iterating to the
next value:
```JavaScript
const pipe = async (source, sink) => {
await source.forEach(sink.push);
sink.close();
};
```
### join(separator) -> (async) String
The values of the channel are converted to Strings, and these Strings are then
concatenated, separated by occurrences of the separator. If no separator is
provided, a single comma is used as the separator.
### length
The length of the channel's buffer.
### map(mapperFunction[, thisArg]) -> Channel
Call `mapperFunction` once for each value in the channel and construct a new
channel with the results.
If `thisArg` is provided it will be used as the `this` value for each invocation
of `mapperFunction`. If it is not provided, `undefined` is used instead.
Unlike in `Array`'s `map` method, `mapperFunction` is called with only one
argument.
### push(value) -> (async) bufferLength
Send the value into the channel and return a promise that resolves when the
value has been shifted or placed in the buffer.
- Throw a `TypeError` when attempting to push to a closed channel.
- Throw a `TypeError` when attempting to push `undefined` because it's a
reserved value used to indicate a closed channel.
The push can be cancelled before completion by calling `cancel` on the returned
promise.
Unlike `Array`'s method, accept only one `value` at a time.
### reduce(callbackfn[, initialValue]) -> (async)
`callbackfn` should be a function that takes two arguments (unlike `Array`'s
version which takes four). `reduce` calls the callback, as a function, once for
each value after the first value present in the channel.
`callbackfn` is called with two arguments: the `previousValue` (value from the
previous call to `callbackfn`) and the `currentValue`. The first time that
callback is called, the `previousValue` and `currentValue` can be one of two
values. If an `initialValue` was provided in the call to `reduce`, then
`previousValue` will be equal to `initialValue` and `currentValue` will be equal
to the first value in the channel. If no `initialValue` was provided, then
`previousValue` will be equal to the first value in the channel and
`currentValue` will be equal to the second. It is a `TypeError` if the channel
contains no values and `initialValue` is not provided.
### shift() -> (async)
Return a promise that resolves when an value is received from the channel.
Closed channels always return `undefined` immediately.
The shift can be cancelled before completion by calling `cancel` on the returned
promise.
### slice(start[, end]) -> Channel
Return a new channel generated by skipping `start` number of values. If `end`
is provided then close the new channel after `end` - `start` values have been
pushed.
Unlike in `Array`'s method, `start` and `end` cannot be negative.
### some(callbackfn[, thisArg])
`callbackfn` should be a function that accepts one argument and returns a value
that is coercible to the Boolean values `true` or `false`. `some` calls
`callbackfn` once for each value in the channel until it finds one where
`callbackfn` returns `true`. If such a value is found, `some` immediately
returns `true`. Otherwise, `some` returns `false`.
If a `thisArg` parameter is provided, it will be used as the `this` value for
each invocation of `callbackfn`. If it is not provided, `undefined` is used
instead.
Unlike in `Array`'s method, `callbackfn` is called with only one argument.
`some` acts like the "exists" quantifier in mathematics. In particular, for an
empty channel, it returns `false`.
### toString() -> String
Return `"Channel(n)"` where `n` is the length of the buffer.
### values() -> (async) iterator
Return an iterator over the values in the channel.
# Functional API
There is a parallel API to support functional-style programming. Every channel
method is also available as an independent function in the `Channel` namespace
that takes a channel as the final argument. For example, `slice` can be called
in either of the following two ways:
```JavaScript
// method
channel.slice(10);
// function
Channel.slice(10, Infinity, channel);
```
You can also use partial application:
```JavaScript
Channel.slice(10, Infinity)(channel);
Channel.slice(10)(Infinity)(channel);
```