diff --git a/.gitignore b/.gitignore index f6ddcd7..c0dea93 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,6 @@ node_modules lib coverage -.rpt2_cache npm-debug.log* yarn-debug.log* yarn-error.log* -lerna-debug.log* diff --git a/packages/limiters/package.json b/packages/limiters/package.json index 2521bc0..27162df 100644 --- a/packages/limiters/package.json +++ b/packages/limiters/package.json @@ -33,7 +33,7 @@ "eslint": "^6.5.1", "eslint-config-prettier": "^6.4.0", "eslint-config-recommended-plus-types": "^1.0.0", - "eslint-plugin-jest": "^22.17.0", + "eslint-plugin-jest": "^22.19.0", "eslint-plugin-prettier": "^3.1.1", "jest": "^24.9.0", "prettier": "^1.18.2", diff --git a/packages/limiters/src/limiters.ts b/packages/limiters/src/limiters.ts index f17ef72..d7d57dc 100644 --- a/packages/limiters/src/limiters.ts +++ b/packages/limiters/src/limiters.ts @@ -95,20 +95,20 @@ export function throttler( let stopped = false; stop.then(() => (stopped = true)); - for await (let token of Repeater.race([semaphore(limit), stop])) { + for await (const token of Repeater.race([semaphore(limit), stop])) { if (stopped) { break; } leaking = leak(); - token = { ...token, reset: start + wait }; - tokens.add(token); + let token1: ThrottleToken = { ...token, reset: start + wait }; + tokens.add(token1); if (cooldown && token.remaining === 0) { await Promise.race([stop, leaking]); - token = { ...token, remaining: limit }; + token1 = { ...token1, remaining: limit }; } - await push(token); + await push(token1); } tokens.clear(); diff --git a/packages/pubsub/package.json b/packages/pubsub/package.json index e7b7d71..db71cf3 100644 --- a/packages/pubsub/package.json +++ b/packages/pubsub/package.json @@ -32,7 +32,7 @@ "eslint": "^6.5.1", "eslint-config-prettier": "^6.4.0", "eslint-config-recommended-plus-types": "^1.0.0", - "eslint-plugin-jest": "^22.17.0", + "eslint-plugin-jest": "^22.19.0", "eslint-plugin-prettier": "^3.1.1", "jest": "^24.9.0", "prettier": "^1.18.2", diff --git a/packages/repeater/package.json b/packages/repeater/package.json index 6eddec3..04876d3 100644 --- a/packages/repeater/package.json +++ b/packages/repeater/package.json @@ -29,7 +29,7 @@ "eslint": "^6.5.1", "eslint-config-prettier": "^6.4.0", "eslint-config-recommended-plus-types": "^1.0.0", - "eslint-plugin-jest": "^22.17.0", + "eslint-plugin-jest": "^22.19.0", "eslint-plugin-prettier": "^3.1.1", "jest": "^24.9.0", "prettier": "^1.18.2", 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 dac095c..2e8b535 100644 --- a/packages/repeater/src/repeater.ts +++ b/packages/repeater/src/repeater.ts @@ -28,36 +28,26 @@ export class RepeaterOverflowError extends Error { } } -// The current definition of AsyncIterator allows "any" to be passed to -// next/return, so we use these type aliases to keep track of the arguments as -// they flow through repeaters. -// TODO: parameterize these types when this PR lands -// (https://github.com/microsoft/TypeScript/pull/30790) Next is the argument -// passed to AsyncIterator.next Return is the argument passed to -// AsyncIterator.return -type Next = any; -type Return = any; - -export type Push = (value: PromiseLike | T) => Promise; - -export interface Stop extends Promise { - (error?: any): void; -} +export type Push = ( + value: PromiseLike | T, +) => Promise; + +export type Stop = Promise & ((error?: any) => void); -export type RepeaterExecutor = ( - push: Push, - stop: Stop, -) => Promise | Return | void; +export type RepeaterExecutor = ( + push: Push, + stop: Stop, +) => PromiseLike | TReturn; -interface PushOperation { - resolve(next?: Next): void; +interface PushOperation { + 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?: Next; + value?: PromiseLike | TNext; } const enum RepeaterState { @@ -72,23 +62,23 @@ const enum RepeaterState { * hidden using a private WeakMap to make repeaters themselves opaque and * maximally compatible with async generators. */ -class RepeaterController implements AsyncIterator { +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?: Next) => void; - // TODO: maybe rename to onreturn - private onstop?: (value?: Return) => void; + private pushQueue: PushOperation[] = []; + private pullQueue: PullOperation[] = []; + private onnext?: (value?: PromiseLike | TNext) => void; + private onstop?: (value?: PromiseLike | TReturn) => void; + private execution?: Promise; + private error?: any; // 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 execution?: Promise | T | void; - private error?: any; + private pending?: Promise>; constructor( - private executor: RepeaterExecutor, - private buffer: RepeaterBuffer | T>, + private executor: RepeaterExecutor, + private buffer: RepeaterBuffer, ) {} /** @@ -102,21 +92,21 @@ class RepeaterController implements AsyncIterator { } this.state = RepeaterState.Started; - const push: Push = this.push.bind(this); - const stop: Stop = this.stop.bind(this) as Stop; - const stopP = new Promise((onstop) => (this.onstop = onstop)); + const push: Push = this.push.bind(this); + const stop: Stop = this.stop.bind(this) as Stop; + const stopP = new Promise((onstop) => (this.onstop = onstop)); stop.then = stopP.then.bind(stopP); stop.catch = stopP.catch.bind(stopP); stop.finally = stopP.finally.bind(stopP); - // Errors which occur in the executor take precedence over those passed to - // this.stop, so calling this.stop with the caught error would be redundant. try { - this.execution = this.executor(push, stop); + this.execution = Promise.resolve(this.executor(push, stop)); } catch (err) { this.execution = Promise.reject(err); } - Promise.resolve(this.execution).catch(() => this.stop()); + // Errors which occur in the executor take precedence over those passed to + // this.stop, so calling this.stop with the caught error would be redundant. + this.execution.catch(() => this.stop()); } /** @@ -124,14 +114,16 @@ class RepeaterController implements AsyncIterator { * 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 reject(err: any): Promise> { if (this.state >= RepeaterState.Stopped) { - const value = await this.execution; - return { value: value as T, done: true }; + return Promise.resolve(this.execution!).then((value) => ({ + value, + done: true, + })); } this.finish().catch(() => {}); - throw err; + return Promise.reject(err); } /** @@ -139,8 +131,10 @@ class RepeaterController implements AsyncIterator { * 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> { - if (this.pending == null) { + private unwrap( + value: PromiseLike | T, + ): Promise> { + if (this.pending === undefined) { this.pending = Promise.resolve(value).then( (value) => { return { value, done: false }; @@ -151,7 +145,7 @@ class RepeaterController implements AsyncIterator { this.pending = this.pending.then( (prev) => { if (prev.done) { - return { done: true } as IteratorResult; + return { value: undefined, done: true } as any; } return Promise.resolve(value).then( @@ -159,7 +153,7 @@ class RepeaterController implements AsyncIterator { (err) => this.reject(err), ); }, - () => ({ done: true } as IteratorResult), + () => ({ value: undefined, done: true } as any), ); } @@ -177,9 +171,11 @@ class RepeaterController implements AsyncIterator { * * Advances state to RepeaterState.Finished. */ - private finish(): Promise> { - const execution = this.execution; + private finish(): Promise> { + const execution = Promise.resolve(this.execution!); const error = this.error; + delete this.execution; + delete this.error; if (this.state < RepeaterState.Finished) { if (this.state < RepeaterState.Stopped) { this.stop(); @@ -188,14 +184,12 @@ class RepeaterController implements AsyncIterator { this.state = RepeaterState.Finished; this.pushQueue = []; this.buffer = new FixedBuffer(0); - delete this.error; - delete this.execution; } - if (this.pending == null) { - this.pending = Promise.resolve(execution).then((value) => { - if (error == null) { - return { value: value as T, done: true }; + if (this.pending === undefined) { + this.pending = execution.then((value) => { + if (error === undefined) { + return { value, done: true }; } throw error; @@ -204,18 +198,18 @@ class RepeaterController implements AsyncIterator { this.pending = this.pending.then( (prev) => { if (prev.done) { - return { done: true } as IteratorResult; + return { value: undefined, done: true } as any; } - return Promise.resolve(execution).then((value) => { - if (error == null) { - return { value: value as T, done: true }; + return execution.then((value) => { + if (error === undefined) { + return { value, done: true }; } throw error; }); }, - () => ({ done: true } as IteratorResult), + () => ({ value: undefined, done: true } as any), ); } @@ -225,10 +219,10 @@ class RepeaterController implements AsyncIterator { /** * This method is bound and passed to the executor as `push`. */ - private push(value: PromiseLike | T): Promise { + private push(value: PromiseLike | T): Promise { Promise.resolve(value).catch(() => {}); if (this.state >= RepeaterState.Stopped) { - return Promise.resolve(); + return Promise.resolve(undefined); } else if (this.pullQueue.length) { const pull = this.pullQueue.shift()!; pull.resolve(this.unwrap(value)); @@ -239,7 +233,7 @@ class RepeaterController implements AsyncIterator { return new Promise((resolve) => (this.onnext = resolve)); } else if (!this.buffer.full) { this.buffer.add(value); - return Promise.resolve(); + return Promise.resolve(undefined); } else if (this.pushQueue.length >= MAX_QUEUE_LENGTH) { throw new RepeaterOverflowError( `No more than ${MAX_QUEUE_LENGTH} pending calls to push are allowed on a single repeater.`, @@ -283,7 +277,9 @@ class RepeaterController implements AsyncIterator { this.pullQueue = []; } - next(value?: Next): Promise> { + next( + value?: PromiseLike | TNext, + ): Promise> { if (this.state === RepeaterState.Initial) { this.execute(); } @@ -319,28 +315,34 @@ class RepeaterController implements AsyncIterator { }); } - return(value?: PromiseLike | Return): Promise> { + return( + value?: PromiseLike | TReturn, + ): Promise> { if (this.state >= RepeaterState.Finished) { - if (this.pending == null) { - this.pending = Promise.resolve({ value, done: true }); + if (this.pending === undefined) { + 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 })); } return this.pending; - } else if (this.onstop != null) { + } + + if (this.onstop !== undefined) { this.onstop(value); } - this.stop(); return this.finish(); } - throw(error: any): Promise> { + throw(error: any): Promise> { if (this.state >= RepeaterState.Finished) { - if (this.pending == null) { + if (this.pending === undefined) { this.pending = Promise.reject(error); } else { this.pending = this.pending.then( @@ -355,60 +357,43 @@ class RepeaterController implements AsyncIterator { this.stop(error); return this.finish(); } -} -export type Contender = AsyncIterable | Iterable | PromiseLike | T; - -function iterators( - contenders: Iterable>, -): (AsyncIterator | Iterator)[] { - const iters: (Iterator | AsyncIterator)[] = []; - for (const contender of contenders) { - if ( - contender != null && - typeof (contender as any)[Symbol.asyncIterator] === "function" - ) { - iters.push((contender as AsyncIterable)[Symbol.asyncIterator]()); - } else if ( - contender != null && - 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 as PromiseLike | T)), - ); - } + [Symbol.asyncIterator](): this { + return this; } - - return iters; } -type RepeaterControllerMap = WeakMap, RepeaterController>; - -const controllers: RepeaterControllerMap = new WeakMap(); - -export class Repeater implements AsyncIterableIterator { +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), + executor: RepeaterExecutor, + buffer: RepeaterBuffer = new FixedBuffer(0), ) { controllers.set(this, new RepeaterController(executor, buffer)); } - next(value?: Next): Promise> { + next(value?: PromiseLike | TNext): Promise> { const controller = controllers.get(this); - if (controller == null) { + if (controller === undefined) { throw new Error("RepeaterController missing from controllers WeakMap"); } return controller.next(value); } - return(value?: PromiseLike | Return): Promise> { + return(value?: PromiseLike | TReturn): Promise> { const controller = controllers.get(this); - if (controller == null) { + if (controller === undefined) { throw new Error("RepeaterController missing from controllers WeakMap"); } @@ -417,7 +402,7 @@ export class Repeater implements AsyncIterableIterator { throw(error?: any): Promise> { const controller = controllers.get(this); - if (controller == null) { + if (controller === undefined) { throw new Error("RepeaterController missing from controllers WeakMap"); } @@ -428,300 +413,328 @@ export class Repeater implements AsyncIterableIterator { 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 */ - // prettier-ignore - static race(contenders: [Contender, Contender, 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]): 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]): Repeater; - // prettier-ignore - static race(contenders: [Contender, Contender, Contender, Contender, Contender]): Repeater; - // prettier-ignore - static race(contenders: [Contender, Contender, Contender, Contender]): Repeater; - // prettier-ignore - static race(contenders: [Contender, Contender, Contender]): Repeater; - // prettier-ignore - static race(contenders: [Contender, Contender]): Repeater; - static race(contenders: [Contender]): Repeater; - static race(contenders: []): 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: Return; - 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; - 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( + new Repeater((_, stop) => (stop(), contender)), + ); + } + } - await push(result1.value); - } + 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 (result === undefined && result1.done) { + stop(); + result = result1; + } + }) + .catch(stop); + } - // prettier-ignore - static merge(contenders: [Contender, Contender, 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]): 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]): Repeater; - // prettier-ignore - static merge(contenders: [Contender, Contender, Contender, Contender, Contender]): Repeater; - // prettier-ignore - static merge(contenders: [Contender, Contender, Contender, Contender]): Repeater; - // prettier-ignore - static merge(contenders: [Contender, Contender, Contender]): Repeater; - // prettier-ignore - static merge(contenders: [Contender, Contender]): Repeater; - static merge(contenders: [Contender]): Repeater; - static merge(contenders: []): 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 (result === undefined && result1.done) { + result = result1; + break; + } - let stopped = false; - let returned: Return; - const finish: Promise> = stop.then((value) => { - stopped = true; - returned = value; - return { value, done: true }; - }); - let value: T | undefined; - await Promise.all( - iters.map(async (iter) => { - try { - while (!stopped) { - const result = await Promise.race([finish, iter.next()]); - if (result.done) { - value = result.value; - return; - } + await push(result1.value as T); + } - await push(result.value); - } - } 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; + } - // 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]>; - // 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]): Repeater<[T1, T2, T3, T4, T5, T6, T7, T8]>; - // 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]): Repeater<[T1, T2, T3, T4, T5, T6]>; - // 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]): Repeater<[T1, T2, T3, T4]>; - // prettier-ignore - static zip(contenders: [Contender, Contender, Contender]): Repeater<[T1, T2, T3]>; - // prettier-ignore - static zip(contenders: [Contender, Contender]): Repeater<[T1, T2]>; - static zip(contenders: [Contender]): Repeater<[T]>; - static zip(contenders: []): Repeater<[]>; - 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: Return; - 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) { + 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 === undefined) { + return returned; + } + return (await iter.return(returned)).value; + }), ); } - } - }); - } - // 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]>; - // 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]): Repeater<[T1, T2, T3, T4, T5, T6, T7, T8]>; - // 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]): Repeater<[T1, T2, T3, T4, T5, T6]>; - // 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]): Repeater<[T1, T2, T3, T4]>; - // prettier-ignore - static latest(contenders: [Contender, Contender, Contender]): Repeater<[T1, T2, T3]>; - // prettier-ignore - static latest(contenders: [Contender, Contender]): Repeater<[T1, T2]>; - static latest(contenders: [Contender]): Repeater<[T]>; - static latest(contenders: []): Repeater<[]>; - 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: Return; - 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 === undefined) { + 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) { + await iter.return(returned); + } + } + }), + ); + stop(); + return result; + }); } diff --git a/packages/timers/package.json b/packages/timers/package.json index 0dce5a6..ac297dd 100644 --- a/packages/timers/package.json +++ b/packages/timers/package.json @@ -32,7 +32,7 @@ "eslint": "^6.5.1", "eslint-config-prettier": "^6.4.0", "eslint-config-recommended-plus-types": "^1.0.0", - "eslint-plugin-jest": "^22.17.0", + "eslint-plugin-jest": "^22.19.0", "eslint-plugin-prettier": "^3.1.1", "jest": "^24.9.0", "prettier": "^1.18.2", diff --git a/yarn.lock b/yarn.lock index 929317f..bf46fc5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1201,10 +1201,10 @@ eslint-config-recommended-plus-types@^1.0.0: resolved "https://registry.yarnpkg.com/eslint-config-recommended-plus-types/-/eslint-config-recommended-plus-types-1.0.0.tgz#aaf8e6d7d6b0cef98b2cf6aae6b7c7c059d2b4c8" integrity sha512-LKFuUDjAmIoSLV+DmyXFHpDg61f3tYFak63r/Od1WrntMwZOOeiSXaiWTsrXh1BK+UUEz3Wr6VUtoJ7i6C6fUA== -eslint-plugin-jest@^22.17.0: - version "22.17.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.17.0.tgz#dc170ec8369cd1bff9c5dd8589344e3f73c88cf6" - integrity sha512-WT4DP4RoGBhIQjv+5D0FM20fAdAUstfYAf/mkufLNTojsfgzc5/IYW22cIg/Q4QBavAZsROQlqppiWDpFZDS8Q== +eslint-plugin-jest@^22.19.0: + version "22.19.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.19.0.tgz#0cf90946a8c927d40a2c64458c89bb635d0f2a0b" + integrity sha512-4zUc3rh36ds0SXdl2LywT4YWA3zRe8sfLhz8bPp8qQPIKvynTTkNGwmSCMpl5d9QiZE2JxSinGF+WD8yU+O0Lg== dependencies: "@typescript-eslint/experimental-utils" "^1.13.0"