UNPKG

ix

Version:

The Interactive Extensions for JavaScript

72 lines (57 loc) 2.12 kB
import { AsyncIterableX } from './asynciterablex.js'; import { wrapWithAbort } from './operators/withabort.js'; import { throwIfAborted } from '../aborterror.js'; import { safeRace } from '../util/safeRace.js'; type MergeResult<T> = { value: T; index: number }; function wrapPromiseWithIndex<T>(promise: Promise<T>, index: number) { return promise.then((value) => ({ value, index })) as Promise<MergeResult<T>>; } /** @ignore */ export class RaceAsyncIterable<TSource> extends AsyncIterableX<TSource> { private _sources: AsyncIterable<TSource>[]; constructor(sources: AsyncIterable<TSource>[]) { super(); this._sources = sources; } async *[Symbol.asyncIterator](signal?: AbortSignal) { throwIfAborted(signal); const sources = this._sources; const length = sources.length; const iterators = new Array<AsyncIterator<TSource>>(length); const nexts = new Array<Promise<MergeResult<IteratorResult<TSource>>>>(length); for (let i = 0; i < length; i++) { const iterator = wrapWithAbort<TSource>(sources[i], signal)[Symbol.asyncIterator](); iterators[i] = iterator; nexts[i] = wrapPromiseWithIndex(iterator.next(), i); } const next = safeRace(nexts); const { value: next$, index } = await next; if (!next$.done) { yield next$.value; } const iterator$ = iterators[index]; // Cancel/finish other iterators for (let i = 0; i < length; i++) { if (i === index) { continue; } const otherIterator = iterators[i]; if (otherIterator.return) { otherIterator.return(); } } let nextItem; while (!(nextItem = await iterator$.next()).done) { yield nextItem.value; } } } /** * Propagates the async sequence that reacts first. * * @param {...AsyncIterable<T>[]} sources The source sequences. * @return {AsyncIterable<T>} An async sequence that surfaces either of the given sequences, whichever reacted first. */ export function race<TSource>(...sources: AsyncIterable<TSource>[]): AsyncIterableX<TSource> { return new RaceAsyncIterable<TSource>(sources); }