UNPKG

spica

Version:

Supervisor, Coroutine, Channel, select, AtomicPromise, Cancellation, Cache, List, Queue, Stack, and some utils.

43 lines (39 loc) 1.91 kB
interface AsyncIterable<T = unknown, U = any, S = unknown> { [Symbol.asyncIterator](): AsyncIterator<T, U, S>; } type Channel = | AsyncIterable<unknown, unknown, undefined> | (() => AsyncIterable<unknown, unknown, undefined>); type Channels = | readonly [] | readonly [Channel, ...Channel[]] | readonly Channel[] | Record<string, Channel>; type ChannelIteratorResult<C extends Channel> = C extends () => AsyncIterable<infer T, infer U> ? IteratorResult<T, U> : C extends AsyncIterable<infer T, infer U> ? IteratorResult<T, U> : never; type Selection<T extends Channels> = T extends readonly unknown[] ? number extends T['length'] ? { readonly name: string; readonly result: T[number] extends Channel ? ChannelIteratorResult<T[number]> : never; } : { [P in keyof T]: T[P] extends Channel ? { readonly name: P; readonly result: ChannelIteratorResult<T[P]>; } : never; }[number] : { [P in keyof T]: T[P] extends Channel ? { readonly name: P; readonly result: ChannelIteratorResult<T[P]>; } : never; }[keyof T]; export async function* select<T extends Channels>( channels: T, ): AsyncGenerator<Selection<T>, undefined, undefined> { const reqs = new Set(Object.entries(channels) .map(([name, chan]) => ( chan = typeof chan === 'function' ? chan() : chan, take(name, chan[Symbol.asyncIterator]())))); while (reqs.size > 0) { const { 0: req, 1: name, 2: chan, 3: result } = await Promise.race(reqs); assert(reqs.has(req)); reqs.delete(req); !result.done && reqs.add(take(name, chan)); yield { name, result } as Selection<T>; } } type Request = Promise<readonly [Request, string, AsyncIterator<unknown, unknown, undefined>, IteratorResult<unknown, unknown>]>; function take(name: string, chan: AsyncIterator<unknown, unknown, undefined>): Request { const req: Request = chan.next().then(result => [req, name, chan, result]); return req; }