UNPKG

thorish

Version:

This is a library of useful JS concepts and data structures for Node and the browser. It it, unashamedly, a dumping ground for code needed by [@samthor](https://twitter.com/samthor)'s projects.

128 lines (127 loc) 5.66 kB
/** * Channel is borrowed from Go. * * It doesn't have a notion of buffered; it's not for communication, just for scheduling. * Channels have infinite buffer space, however, you can use {@link Channel.push} to wait for a pushed value to be consumed. * * It's similar to but subtly different from a {@link AsyncGenerator}. * Whereas calling {@link AsyncGenerator.next} repeatedly creates a {@link Promise} of the next value, here, we instead wait for a value to be available before choosing to obtain it. * In this way it lends itself to "pull" semantics; the consumer of one or many {@link Channel} instances can decide how to proceed. */ export type Channel<T> = { /** * Push this value into the {@link Channel}. * Returns a {@link Promise} which resolves when it is consumed (optional). */ push(t: T): Promise<void>; /** * Closes this channel. * No more values can be pushed, and the given value will be provided in perpetuity (i.e., will always be available). * * You must disambiguate T yourself to identify a closed channel. */ close(t: T): void; /** * Waits until a value from this channel is available. * * This is as {@link ReadChannel.wait}, but with a wider type. */ wait<V = Channel<T>>(value?: V): Promise<V>; } & ReadChannel<T>; export type ReadChannel<T> = { /** * Returns whether this channel is closed _and_ the close value has been provided via {@link ReadChannel.next}. * * This can be used to e.g., prematurely exit a loop or check what value was just retrieved. */ readonly closed: boolean; /** * Waits until a value from this channel is available. * * Repeated calls to `wait()` will delay a microtask before resolving. * This allows a resolved task to safely consume a value synchronously after this resolves. * * For no other reason than convenience, this resolves with itself, unless you include another value as an argument. * The assumption is you can then synchronously consume the next value with {@link ReadChannel#next}. */ wait<V = ReadChannel<T>>(value?: V): Promise<V>; /** * Returns whether {@link ReadChannel#next} has an available value. */ pending(): boolean; /** * Consumes a value from this {@link ReadChannel}, if possible. * Otherwise, returns `undefined`. */ next(): T | undefined; }; type MessageType<Q> = Q extends ReadChannel<infer X> ? X : never; export type SelectRequest = { [key: string | symbol]: ReadChannel<any> | undefined; }; export type SelectResult<TChannels extends SelectRequest> = { [TKey in keyof TChannels]: Readonly<{ key: TKey; ch: NonNullable<TChannels[TKey]>; m: MessageType<TChannels[TKey]>; closed: boolean; }>; }[keyof TChannels]; export type ReadyResult<TChannels extends SelectRequest> = { [TKey in keyof TChannels]: Readonly<{ key: TKey; ch: NonNullable<TChannels[TKey]>; closed: boolean; }>; }[keyof TChannels]; /** * Waits for the first {@link Channel} that is ready based on key, reading that channel. * Returns with the key for matching. * * For safety, throws if no valid channels are passed (zero channels or all `undefined`). * * This uses JS' default object ordering for what is "first" when many are ready: integers >= 0 in order, all others, symbols. */ export declare function select<TChannels extends SelectRequest>(o: TChannels): Promise<SelectResult<TChannels>>; /** * Waits for the first {@link Channel} that is ready based on key, reading that channel. * Returns with the key for matching. * Prefers the passed {@link AbortSignal}, which if aborted, returns `undefined`. * * Does not throw if missing channels, as the {@link AbortSignal} is an implied 'channel'. * * This uses JS' default object ordering for what is "first" when many are ready: integers >= 0 in order, all others, symbols. */ export declare function select<TChannels extends SelectRequest>(o: TChannels, signal: AbortSignal): Promise<SelectResult<TChannels> | undefined>; export declare function select<TChannels extends SelectRequest>(o: TChannels): Promise<SelectResult<TChannels>>; /** * Selects the first {@link ReadChannel} that is ready, reading that channel, or `undefined` if none are ready. * Returns with the key for matching. * * This uses JS' default object ordering for what is "first" when many are ready: integers >= 0 in order, all others, symbols. */ export declare function selectDefault<TChannels extends { [key: string | symbol]: ReadChannel<any> | undefined; }>(o: TChannels): SelectResult<TChannels> | undefined; /** * Selects the first {@link ReadChannel} that is ready, or `undefined` if none are ready. * Returns with the key for matching. * * This uses JS' default object ordering for what is "first" when many are ready: integers >= 0 in order, all others, symbols. */ export declare function readyDefault<TChannels extends { [key: string | symbol]: ReadChannel<any> | undefined; }>(o: TChannels): ReadyResult<TChannels> | undefined; /** * Builds a new {@link Channel}. */ export declare function newChannel<T>(): Channel<T>; /** * Converts a {@link AbortSignal} into a channel which is closed when it aborts. */ export declare function channelForSignal<T = AbortSignal>(s: AbortSignal, v?: T): ReadChannel<T>; /** * Converts a {@link AsyncGenerator} to a channel which contains {@link IteratorResult}. */ export declare function channelForGenerator<T, TReturn>(g: AsyncGenerator<T, TReturn, void>): ReadChannel<IteratorResult<T, TReturn>>; export {};