@ayonli/jsext
Version:
A JavaScript extension package for building strong and modern applications.
166 lines (165 loc) • 5.2 kB
TypeScript
/**
* A channel implementation that transfers data across routines, even across
* multiple threads, inspired by Golang.
* @module
*/
import { id } from "./env.ts";
/**
* A channel implementation that transfers data across routines, even across
* multiple threads, inspired by Golang.
*/
export declare class Channel<T> implements AsyncIterable<T> {
readonly [id]: number;
/** The capacity is the maximum number of data allowed to be buffered. */
readonly capacity: number;
private buffer;
private producers;
private consumers;
private error?;
private state;
constructor(capacity?: number);
/**
* Pushes data to the channel.
*
* If there is a receiver, the data will be consumed immediately. Otherwise:
*
* - If this is an non-buffered channel, this function will block until a
* receiver is available and the data is consumed.
*
* - If this is a buffered channel, then:
* - If the buffer size is within the capacity, the data will be pushed
* to the buffer.
* - Otherwise, this function will block until there is new space for
* the data in the buffer.
*/
send(data: T): Promise<void>;
/**
* Retrieves data from the channel.
*
* If there isn't data available at the moment, this function will block
* until new data is available.
*
* If the channel is closed, then:
*
* - If there is error set in the channel, this function throws that error
* immediately.
* - Otherwise, this function returns `undefined` immediately.
*/
recv(): Promise<T | undefined>;
/**
* Closes the channel. If `err` is supplied, it will be captured by the
* receiver.
*
* No more data shall be sent once the channel is closed.
*
* Explicitly closing the channel is not required, if the channel is no
* longer used, it will be automatically released by the GC. However, if
* the channel is used in a `for await...of...` loop, closing the channel
* will allow the loop to break automatically.
*
* Moreover, if the channel is used between parallel threads, it will no
* longer be able to release automatically, must explicitly call this
* function in order to release for GC.
*/
close(err?: Error | null): void;
[Symbol.asyncIterator](): {
next(): Promise<IteratorResult<T>>;
};
[Symbol.dispose](): void;
/** @deprecated This method is deprecated in favor of the `send()` method. */
push(data: T): Promise<void>;
/** @deprecated This method is deprecated in favor of the `recv()` method. */
pop(): Promise<T | undefined>;
}
/**
* Inspired by Golang, cerates a {@link Channel} that can be used to transfer
* data across routines.
*
* If `capacity` is not set, a non-buffered channel will be created. For a
* non-buffered channel, the sender and receiver must be present at the same
* time (theoretically), otherwise, the channel will block (non-IO aspect).
*
* If `capacity` is set, a buffered channel will be created. For a buffered
* channel, data will be queued in the buffer first and then consumed by the
* receiver in FIFO order. Once the buffer size reaches the capacity limit, no
* more data will be sent unless there is new space available.
*
* It is possible to set the `capacity` to `Infinity` to allow the channel to
* never block and behave like a message queue.
*
* Unlike `EventEmitter` or `EventTarget`, `Channel` guarantees the data will
* always be delivered, even if there is no receiver at the moment.
*
* Also, unlike Golang, `await channel.recv()` does not prevent the program from
* exiting.
*
* Channels can be used to send and receive streaming data between main thread
* and worker threads wrapped by `parallel()`, but once used that way,
* `channel.close()` must be explicitly called in order to release the channel
* for garbage collection.
*
* @example
* ```ts
* // non-buffered
* import chan from "@ayonli/jsext/chan";
*
* const channel = chan<number>();
*
* (async () => {
* await channel.send(123);
* })();
*
* const num = await channel.recv();
* console.log(num); // 123
* // output:
* // 123
* ```
*
* @example
* ```ts
* // buffered
* import chan from "@ayonli/jsext/chan";
*
* const channel = chan<number>(3);
*
* await channel.send(123);
* await channel.send(456);
* await channel.send(789);
*
* const num1 = await channel.recv();
* const num2 = await channel.recv();
* const num3 = await channel.recv();
*
* console.log(num1); // 123
* console.log(num2); // 456
* console.log(num3); // 789
* ```
*
* @example
* ```ts
* // iterable
* import chan from "@ayonli/jsext/chan";
* import { range } from "@ayonli/jsext/number";
*
* const channel = chan<number>();
*
* (async () => {
* for (const num of range(1, 5)) {
* await channel.send(num);
* }
*
* channel.close();
* })();
*
* for await (const num of channel) {
* console.log(num);
* }
* // output:
* // 1
* // 2
* // 3
* // 4
* // 5
* ```
*/
export default function chan<T>(capacity?: number): Channel<T>;