From 6c919025b74f9efa4acd911a026d57d3058ba701 Mon Sep 17 00:00:00 2001 From: Brian Kim Date: Sun, 13 Oct 2019 03:47:08 -0400 Subject: [PATCH] refine types --- packages/repeater/src/buffers.ts | 22 +- packages/repeater/src/repeater.ts | 676 +++++++++++++++--------------- 2 files changed, 358 insertions(+), 340 deletions(-) diff --git a/packages/repeater/src/buffers.ts b/packages/repeater/src/buffers.ts index 61b5a43..ecac3ef 100644 --- a/packages/repeater/src/buffers.ts +++ b/packages/repeater/src/buffers.ts @@ -1,12 +1,12 @@ export interface RepeaterBuffer { full: boolean; empty: boolean; - add(value: T): void; - remove(): T; + add(value: PromiseLike | T): void; + remove(): PromiseLike | T; } export class FixedBuffer implements RepeaterBuffer { - private arr: T[] = []; + private arr: (PromiseLike | T)[] = []; get empty(): boolean { return this.arr.length === 0; @@ -22,7 +22,7 @@ export class FixedBuffer implements RepeaterBuffer { } } - add(value: T): void { + add(value: PromiseLike | T): void { if (this.full) { throw new Error("Buffer full"); } else { @@ -30,7 +30,7 @@ export class FixedBuffer implements RepeaterBuffer { } } - remove(): T { + remove(): PromiseLike | T { if (this.empty) { throw new Error("Buffer empty"); } @@ -41,7 +41,7 @@ export class FixedBuffer implements RepeaterBuffer { // TODO: use a circular buffer here export class SlidingBuffer implements RepeaterBuffer { - private arr: T[] = []; + private arr: (PromiseLike | T)[] = []; get empty(): boolean { return this.arr.length === 0; @@ -57,7 +57,7 @@ export class SlidingBuffer implements RepeaterBuffer { } } - add(value: T): void { + add(value: PromiseLike | T): void { while (this.arr.length >= this.capacity) { this.arr.shift(); } @@ -65,7 +65,7 @@ export class SlidingBuffer implements RepeaterBuffer { this.arr.push(value); } - remove(): T { + remove(): PromiseLike | T { if (this.empty) { throw new Error("Buffer empty"); } @@ -75,7 +75,7 @@ export class SlidingBuffer implements RepeaterBuffer { } export class DroppingBuffer implements RepeaterBuffer { - private arr: T[] = []; + private arr: (PromiseLike | T)[] = []; get empty(): boolean { return this.arr.length === 0; @@ -91,13 +91,13 @@ export class DroppingBuffer implements RepeaterBuffer { } } - add(value: T): void { + add(value: PromiseLike | T): void { if (this.arr.length < this.capacity) { this.arr.push(value); } } - remove(): T { + remove(): PromiseLike | T { if (this.empty) { throw new Error("Buffer empty"); } diff --git a/packages/repeater/src/repeater.ts b/packages/repeater/src/repeater.ts index 2e46397..c26b6d8 100644 --- a/packages/repeater/src/repeater.ts +++ b/packages/repeater/src/repeater.ts @@ -41,14 +41,14 @@ export type RepeaterExecutor = ( ) => PromiseLike | TReturn; interface PushOperation { - resolve(next?: TNext): void; + resolve(next?: PromiseLike | TNext): void; value: PromiseLike | T; } -interface PullOperation { - resolve(result: Promise>): void; +interface PullOperation { + resolve(result: Promise>): void; reject(err?: any): void; - value?: TNext; + value?: PromiseLike | TNext; } const enum RepeaterState { @@ -63,23 +63,24 @@ const enum RepeaterState { * hidden using a private WeakMap to make repeaters themselves opaque and * maximally compatible with async generators. */ -class RepeaterController { +class RepeaterController + implements AsyncGenerator { private state: RepeaterState = RepeaterState.Initial; // pushQueue and pullQueue will never both contain operations at the same time private pushQueue: PushOperation[] = []; - private pullQueue: PullOperation[] = []; - private onnext?: (value?: TNext) => void; + private pullQueue: PullOperation[] = []; + private onnext?: (value?: PromiseLike | TNext) => void; // TODO: maybe rename to onreturn private onstop?: (value?: PromiseLike | TReturn) => void; // pending is continuously reassigned as the repeater is iterated. We use // this mechanism to make sure all iterations settle in order. - private pending?: Promise>; + private pending?: Promise>; private execution?: Promise; private error?: any; constructor( private executor: RepeaterExecutor, - private buffer: RepeaterBuffer | T>, + private buffer: RepeaterBuffer, ) {} /** @@ -115,9 +116,9 @@ class RepeaterController { * Rejections which settle after stop are ignored. This behavior is useful * when you have yielded a pending promise but want to finish instead. */ - private async reject(err: any): Promise> { + private async reject(err: any): Promise> { if (this.state >= RepeaterState.Stopped) { - const value = await this.execution; + const value = await this.execution!; return { value, done: true }; } @@ -130,7 +131,9 @@ class RepeaterController { * prevents types of Repeater> and mimics the awaiting/unwrapping * behavior of async generators where `yield` is equivalent to `yield await`. */ - private unwrap(value: PromiseLike | T): Promise> { + private unwrap( + value: PromiseLike | T, + ): Promise> { if (this.pending == null) { this.pending = Promise.resolve(value).then( (value) => { @@ -142,7 +145,7 @@ class RepeaterController { this.pending = this.pending.then( (prev) => { if (prev.done) { - return { value: undefined, done: true }; + return { value: undefined, done: true } as any; } return Promise.resolve(value).then( @@ -150,7 +153,7 @@ class RepeaterController { (err) => this.reject(err), ); }, - () => ({ value: undefined, done: true }), + () => ({ value: undefined, done: true } as any), ); } @@ -168,8 +171,8 @@ class RepeaterController { * * Advances state to RepeaterState.Finished. */ - private finish(): Promise> { - const execution = Promise.resolve(this.execution); + private finish(): Promise> { + const execution = Promise.resolve(this.execution!); const error = this.error; if (this.state < RepeaterState.Finished) { if (this.state < RepeaterState.Stopped) { @@ -195,7 +198,7 @@ class RepeaterController { this.pending = this.pending.then( (prev) => { if (prev.done) { - return { value: undefined, done: true }; + return { value: undefined, done: true } as any; } return execution.then((value) => { @@ -206,7 +209,7 @@ class RepeaterController { throw error; }); }, - () => ({ value: undefined, done: true }), + () => ({ value: undefined, done: true } as any), ); } @@ -274,7 +277,9 @@ class RepeaterController { this.pullQueue = []; } - next(value?: TNext): Promise> { + next( + value?: PromiseLike | TNext, + ): Promise> { if (this.state === RepeaterState.Initial) { this.execute(); } @@ -310,13 +315,18 @@ class RepeaterController { }); } - return(value?: PromiseLike | TReturn): Promise> { + return( + value?: PromiseLike | TReturn, + ): Promise> { if (this.state >= RepeaterState.Finished) { if (this.pending == null) { - this.pending = Promise.resolve({ value, done: true }); + this.pending = Promise.resolve(value).then((value) => ({ + value: value!, + done: true, + })); } else { this.pending = this.pending - .then(() => value) + .then(() => value!) .then((value) => ({ value, done: true })); } @@ -329,7 +339,7 @@ class RepeaterController { return this.finish(); } - throw(error: any): Promise> { + throw(error: any): Promise> { if (this.state >= RepeaterState.Finished) { if (this.pending == null) { this.pending = Promise.reject(error); @@ -346,50 +356,32 @@ class RepeaterController { this.stop(error); return this.finish(); } -} -// TODO: parameterize TReturn -type Contender = AsyncIterable | Iterable | PromiseLike; - -function iterators( - contenders: Iterable>, -): (AsyncIterator | Iterator)[] { - const iters: (AsyncIterator | Iterator)[] = []; - for (const contender of contenders) { - if (typeof (contender as any)[Symbol.asyncIterator] === "function") { - iters.push((contender as AsyncIterable)[Symbol.asyncIterator]()); - } else if (typeof (contender as any)[Symbol.iterator] === "function") { - iters.push((contender as Iterable)[Symbol.iterator]()); - } else { - iters.push( - // eslint-disable-next-line @typescript-eslint/no-use-before-define - new Repeater((_, stop) => (stop(), contender)), - ); - } + [Symbol.asyncIterator](): this { + return this; } - - return iters; } -type RepeaterControllerMap = WeakMap< - Repeater, - RepeaterController ->; - -const controllers: RepeaterControllerMap = new WeakMap(); - +const controllers = new WeakMap< + Repeater, + RepeaterController +>(); + +// We do not export any types which use the >=3.6 IteratorResult, AsyncIterator +// or AsyncGenerator types to allow the library to be used with older versions +// of typescript. +// +// TODO: use typesVersions to ship stricter types for newer typescript +// versions. export class Repeater { constructor( executor: RepeaterExecutor, - buffer: RepeaterBuffer | T> = new FixedBuffer(0), + buffer: RepeaterBuffer = new FixedBuffer(0), ) { - controllers.set( - this, - new RepeaterController(executor, buffer), - ); + controllers.set(this, new RepeaterController(executor, buffer)); } - next(value?: TNext): Promise> { + next(value?: PromiseLike | TNext): Promise> { const controller = controllers.get(this); if (controller == null) { throw new Error("RepeaterController missing from controllers WeakMap"); @@ -420,303 +412,329 @@ export class Repeater { return this; } - // TODO: rethink the done value for each of the combinators - // TODO: remove eslint-disable comments once no-dupe-class-members is fixed - // https://github.com/typescript-eslint/typescript-eslint/issues/291 - // TODO: use prettier-ignore-start/prettier-ignore-end once it’s implemented - // https://github.com/prettier/prettier/issues/5287 - // TODO: stop using overloads once we have variadic kinds - // https://github.com/Microsoft/TypeScript/issues/5453 - /* eslint-disable no-dupe-class-members */ - static race(contenders: []): Repeater; - static race(contenders: Iterable>): Repeater; - // prettier-ignore - static race(contenders: [Contender, Contender]): Repeater; - // prettier-ignore - static race(contenders: [Contender, Contender, Contender]): Repeater; - // prettier-ignore - static race(contenders: [Contender, Contender, Contender, Contender]): Repeater; - // prettier-ignore - static race(contenders: [Contender, Contender, Contender, Contender, Contender]): Repeater; - // prettier-ignore - static race(contenders: [Contender, Contender, Contender, Contender, Contender, Contender]): Repeater; - // prettier-ignore - static race(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater; - // prettier-ignore - static race(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater; - // prettier-ignore - static race(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater; - // prettier-ignore - static race(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater; - static race(contenders: Iterable>): Repeater { - const iters = iterators(contenders); - return new Repeater(async (push, stop) => { - if (!iters.length) { - stop(); - return; - } + static race = race; + static merge = merge; + static zip = zip; + static latest = latest; +} - let stopped = false; - let returned: any; - const finish: Promise> = stop.then((value) => { - stopped = true; - returned = value; - return { value, done: true }; - }); - try { - let result: IteratorResult | undefined; - while (!stopped) { - const results = iters.map((iter) => iter.next()); - for (const result1 of results) { - Promise.resolve(result1) - .then((result1) => { - if (result1.done && result == null) { - stop(); - result = result1; - } - }) - .catch(stop); - } +// TODO: parameterize TReturn +type Contender = AsyncIterable | Iterable | PromiseLike | any; - results.unshift(finish); - const result1 = await Promise.race(results); - if (result1.done && result == null) { - result = result1; - break; - } +function iterators( + contenders: Iterable>, +): (AsyncIterator | Iterator)[] { + const iters: (AsyncIterator | Iterator)[] = []; + for (const contender of contenders) { + if (typeof (contender as any)[Symbol.asyncIterator] === "function") { + iters.push((contender as AsyncIterable)[Symbol.asyncIterator]()); + } else if (typeof (contender as any)[Symbol.iterator] === "function") { + iters.push((contender as Iterable)[Symbol.iterator]()); + } else { + iters.push( + // eslint-disable-next-line @typescript-eslint/no-use-before-define + new Repeater((_, stop) => (stop(), contender)), + ); + } + } - await push(result1.value as T); - } + return iters; +} - return result && result.value; - } catch (err) { - stop(err); - } finally { - stop(); - await Promise.race( - iters.map((iter) => iter.return && iter.return(returned)), - ); - } +// TODO: rethink the done value for each of the combinators +// TODO: parameterize TReturn types +// TODO: use prettier-ignore-start/prettier-ignore-end once it’s implemented +// https://github.com/prettier/prettier/issues/5287 +// TODO: stop using overloads once we have variadic kinds +// https://github.com/Microsoft/TypeScript/issues/5453 +function race(contenders: []): Repeater; +function race(contenders: Iterable>): Repeater; +// prettier-ignore +function race(contenders: [Contender, Contender]): Repeater; +// prettier-ignore +function race(contenders: [Contender, Contender, Contender]): Repeater; +// prettier-ignore +function race(contenders: [Contender, Contender, Contender, Contender]): Repeater; +// prettier-ignore +function race(contenders: [Contender, Contender, Contender, Contender, Contender]): Repeater; +// prettier-ignore +function race(contenders: [Contender, Contender, Contender, Contender, Contender, Contender]): Repeater; +// prettier-ignore +function race(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater; +// prettier-ignore +function race(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater; +// prettier-ignore +function race(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater; +// prettier-ignore +function race(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater; +function race(contenders: Iterable>): Repeater { + const iters = iterators(contenders); + return new Repeater(async (push, stop) => { + if (!iters.length) { + stop(); + return; + } + + let stopped = false; + let returned: any; + const finish: Promise> = stop.then((value) => { + stopped = true; + returned = value; + return { value, done: true }; }); - } + try { + let result: IteratorResult | undefined; + while (!stopped) { + const results = iters.map((iter) => iter.next()); + for (const result1 of results) { + Promise.resolve(result1) + .then((result1) => { + if (result1.done && result == null) { + stop(); + result = result1; + } + }) + .catch(stop); + } - static merge(contenders: []): Repeater; - static merge(contenders: Iterable>): Repeater; - // prettier-ignore - static merge(contenders: [Contender, Contender]): Repeater; - // prettier-ignore - static merge(contenders: [Contender, Contender, Contender]): Repeater; - // prettier-ignore - static merge(contenders: [Contender, Contender, Contender, Contender]): Repeater; - // prettier-ignore - static merge(contenders: [Contender, Contender, Contender, Contender, Contender]): Repeater; - // prettier-ignore - static merge(contenders: [Contender, Contender, Contender, Contender, Contender, Contender]): Repeater; - // prettier-ignore - static merge(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater; - // prettier-ignore - static merge(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater; - // prettier-ignore - static merge(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater; - // prettier-ignore - static merge(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater; - static merge(contenders: Iterable>): Repeater { - const iters = iterators(contenders); - return new Repeater(async (push, stop) => { - if (!iters.length) { - stop(); - return; - } + results.unshift(finish); + const result1 = await Promise.race(results); + if (result1.done && result == null) { + result = result1; + break; + } - let stopped = false; - let returned: any; - const finish: Promise> = stop.then((value) => { - stopped = true; - returned = value; - return { value, done: true }; - }); - let value: any | undefined; - await Promise.all( - iters.map(async (iter) => { - try { - while (!stopped) { - const result: IteratorResult = await Promise.race([ - finish, - iter.next(), - ]); - if (result.done) { - value = result.value; - return; - } + await push(result1.value as T); + } - await push(result.value as T); - } - } catch (err) { - stop(err); - } finally { - if (iter.return != null) { - await iter.return(returned); - } - } - }), + return result && result.value; + } catch (err) { + stop(err); + } finally { + stop(); + await Promise.race( + iters.map((iter) => iter.return && iter.return(returned)), ); + } + }); +} + +function merge(contenders: []): Repeater; +function merge(contenders: Iterable>): Repeater; +// prettier-ignore +function merge(contenders: [Contender, Contender]): Repeater; +// prettier-ignore +function merge(contenders: [Contender, Contender, Contender]): Repeater; +// prettier-ignore +function merge(contenders: [Contender, Contender, Contender, Contender]): Repeater; +// prettier-ignore +function merge(contenders: [Contender, Contender, Contender, Contender, Contender]): Repeater; +// prettier-ignore +function merge(contenders: [Contender, Contender, Contender, Contender, Contender, Contender]): Repeater; +// prettier-ignore +function merge(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater; +// prettier-ignore +function merge(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater; +// prettier-ignore +function merge(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater; +// prettier-ignore +function merge(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater; +function merge(contenders: Iterable>): Repeater { + // need to pass type parameter here for some reason + const iters = iterators(contenders); + return new Repeater(async (push, stop) => { + if (!iters.length) { stop(); - return value; - }); - } + return; + } - static zip(contenders: []): Repeater; - static zip(contenders: Iterable>): Repeater; - // prettier-ignore - static zip(contenders: [Contender, Contender]): Repeater<[T1, T2]>; - // prettier-ignore - static zip(contenders: [Contender, Contender, Contender]): Repeater<[T1, T2, T3]>; - // prettier-ignore - static zip(contenders: [Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4]>; - // prettier-ignore - static zip(contenders: [Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5]>; - // prettier-ignore - static zip(contenders: [Contender, Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5, T6]>; - // prettier-ignore - static zip(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5, T6, T7]>; - // prettier-ignore - static zip(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5, T6, T7, T8]>; - // prettier-ignore - static zip(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>; - // prettier-ignore - static zip(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>; - static zip(contenders: Iterable>): Repeater { - const iters = iterators(contenders); - return new Repeater(async (push, stop) => { - if (!iters.length) { - stop(); - return []; - } + let stopped = false; + let returned: any; + const finish: Promise> = stop.then((value) => { + stopped = true; + returned = value; + return { value, done: true }; + }); + let value: any | undefined; + await Promise.all( + iters.map(async (iter) => { + try { + while (!stopped) { + const result: IteratorResult = await Promise.race([ + finish, + iter.next(), + ]); + if (result.done) { + value = result.value; + return; + } - let stopped = false; - let returned: any; - stop.then((value) => { - stopped = true; - returned = value; - }); - try { - while (!stopped) { - const resultsP = Promise.all(iters.map((iter) => iter.next())); - await Promise.race([stop, resultsP]); - if (stopped) { - return Promise.all( - iters.map(async (iter) => { - if (iter.return == null) { - return returned; - } - return (await iter.return(returned)).value; - }), - ); + await push(result.value as T); } - - const results = await resultsP; - const values = results.map((result) => result.value); - if (results.some((result) => result.done)) { - return values; + } catch (err) { + stop(err); + } finally { + if (iter.return != null) { + await iter.return(returned); } - - await push(values); } - } catch (err) { - stop(err); - } finally { - stop(); - if (!stopped) { - await Promise.all( - iters.map((iter) => iter.return && iter.return(returned)), + }), + ); + stop(); + return value; + }); +} + +function zip(contenders: []): Repeater; +function zip(contenders: Iterable>): Repeater; +// prettier-ignore +function zip(contenders: [Contender, Contender]): Repeater<[T1, T2]>; +// prettier-ignore +function zip(contenders: [Contender, Contender, Contender]): Repeater<[T1, T2, T3]>; +// prettier-ignore +function zip(contenders: [Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4]>; +// prettier-ignore +function zip(contenders: [Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5]>; +// prettier-ignore +function zip(contenders: [Contender, Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5, T6]>; +// prettier-ignore +function zip(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5, T6, T7]>; +// prettier-ignore +function zip(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5, T6, T7, T8]>; +// prettier-ignore +function zip(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>; +// prettier-ignore +function zip(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>; +function zip(contenders: Iterable>): Repeater { + const iters = iterators(contenders); + return new Repeater(async (push, stop) => { + if (!iters.length) { + stop(); + return []; + } + + let stopped = false; + let returned: any; + stop.then((value) => { + stopped = true; + returned = value; + }); + try { + while (!stopped) { + const resultsP = Promise.all(iters.map((iter) => iter.next())); + await Promise.race([stop, resultsP]); + if (stopped) { + return Promise.all( + iters.map(async (iter) => { + if (iter.return == null) { + return returned; + } + return (await iter.return(returned)).value; + }), ); } - } - }); - } - static latest(contenders: []): Repeater; - static latest(contenders: Iterable>): Repeater; - // prettier-ignore - static latest(contenders: [Contender, Contender]): Repeater<[T1, T2]>; - // prettier-ignore - static latest(contenders: [Contender, Contender, Contender]): Repeater<[T1, T2, T3]>; - // prettier-ignore - static latest(contenders: [Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4]>; - // prettier-ignore - static latest(contenders: [Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5]>; - // prettier-ignore - static latest(contenders: [Contender, Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5, T6]>; - // prettier-ignore - static latest(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5, T6, T7]>; - // prettier-ignore - static latest(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5, T6, T7, T8]>; - // prettier-ignore - static latest(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>; - // prettier-ignore - static latest(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>; - static latest(contenders: Iterable>): Repeater { - const iters = iterators(contenders); - return new Repeater(async (push, stop) => { - if (!iters.length) { - stop(); - return []; - } + const results = await resultsP; + const values = results.map((result) => result.value); + if (results.some((result) => result.done)) { + return values; + } - let stopped = false; - let returned: any; - const finish = stop.then((value) => { - stopped = true; - returned = value; - return { value, done: true }; - }); - const resultsP = Promise.all(iters.map((iter) => iter.next())); - await Promise.race([stop, resultsP]); - if (stopped) { - return Promise.all( - iters.map(async (iter) => { - if (iter.return == null) { - return returned; - } - return (await iter.return(returned)).value; - }), + await push(values); + } + } catch (err) { + stop(err); + } finally { + stop(); + if (!stopped) { + await Promise.all( + iters.map((iter) => iter.return && iter.return(returned)), ); } + } + }); +} - const results = await resultsP; - const values = results.map((result) => result.value); - if (results.every((result) => result.done)) { - return values; - } +function latest(contenders: []): Repeater; +function latest(contenders: Iterable>): Repeater; +// prettier-ignore +function latest(contenders: [Contender, Contender]): Repeater<[T1, T2]>; +// prettier-ignore +function latest(contenders: [Contender, Contender, Contender]): Repeater<[T1, T2, T3]>; +// prettier-ignore +function latest(contenders: [Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4]>; +// prettier-ignore +function latest(contenders: [Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5]>; +// prettier-ignore +function latest(contenders: [Contender, Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5, T6]>; +// prettier-ignore +function latest(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5, T6, T7]>; +// prettier-ignore +function latest(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5, T6, T7, T8]>; +// prettier-ignore +function latest(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>; +// prettier-ignore +function latest(contenders: [Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender, Contender]): Repeater<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>; +function latest(contenders: Iterable>): Repeater { + const iters = iterators(contenders); + return new Repeater(async (push, stop) => { + if (!iters.length) { + stop(); + return []; + } - await push(values.slice()); - const result = await Promise.all( - iters.map(async (iter, i) => { - if (results[i].done) { - return results[i].value; + let stopped = false; + let returned: any; + const finish = stop.then((value) => { + stopped = true; + returned = value; + return { value, done: true }; + }); + const resultsP = Promise.all(iters.map((iter) => iter.next())); + await Promise.race([stop, resultsP]); + if (stopped) { + return Promise.all( + iters.map(async (iter) => { + if (iter.return == null) { + return returned; } - try { - while (!stopped) { - const result = await Promise.race([finish, iter.next()]); - if (result.done) { - return result.value; - } + return (await iter.return(returned)).value; + }), + ); + } - values[i] = result.value; - await push(values.slice()); - } - } catch (err) { - stop(err); - } finally { - if (iter.return != null) { - await iter.return(returned); + const results = await resultsP; + const values = results.map((result) => result.value); + if (results.every((result) => result.done)) { + return values; + } + + await push(values.slice()); + const result = await Promise.all( + iters.map(async (iter, i) => { + if (results[i].done) { + return results[i].value; + } + try { + while (!stopped) { + const result = await Promise.race([finish, iter.next()]); + if (result.done) { + return result.value; } + + values[i] = result.value; + await push(values.slice()); } - }), - ); - stop(); - return result; - }); - } - /* eslint-enable no-dupe-class-members */ + } catch (err) { + stop(err); + } finally { + if (iter.return != null) { + await iter.return(returned); + } + } + }), + ); + stop(); + return result; + }); }