Skip to content

Commit 3469e70

Browse files
committed
fix(asynciterable): use more yield
1 parent 7221be2 commit 3469e70

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+325
-276
lines changed

src/asynciterable/_extremaby.ts

+21-19
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,31 @@ export async function extremaBy<TSource, TKey>(
99
): Promise<TSource[]> {
1010
throwIfAborted(signal);
1111

12-
let result = [];
13-
const it = wrapWithAbort(source, signal)[Symbol.asyncIterator]();
14-
const { value, done } = await it.next();
15-
if (done) {
16-
throw new Error('Sequence contains no elements');
17-
}
18-
19-
let resKey = await selector(value, signal);
20-
result.push(value);
12+
let hasValue = false;
13+
let key: TKey | undefined;
14+
let result: TSource[] = [];
2115

22-
let next: IteratorResult<TSource>;
23-
while (!(next = await it.next()).done) {
24-
const current = next.value;
25-
const key = await selector(current, signal);
26-
const cmp = await comparer(key, resKey, signal);
16+
for await (const item of wrapWithAbort(source, signal)) {
17+
if (!hasValue) {
18+
key = await selector(item, signal);
19+
result.push(item);
20+
hasValue = true;
21+
} else {
22+
const currentKey = await selector(item, signal);
23+
const cmp = await comparer(currentKey, key as TKey, signal);
2724

28-
if (cmp === 0) {
29-
result.push(current);
30-
} else if (cmp > 0) {
31-
result = [current];
32-
resKey = key;
25+
if (cmp === 0) {
26+
result.push(item);
27+
} else if (cmp > 0) {
28+
result = [item];
29+
key = currentKey;
30+
}
3331
}
3432
}
3533

34+
if (!hasValue) {
35+
throw new Error('Sequence contains no elements');
36+
}
37+
3638
return result;
3739
}

src/asynciterable/average.ts

+3
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,12 @@ export async function average(
4242
['signal']: signal,
4343
['thisArg']: thisArg,
4444
} = options || {};
45+
4546
throwIfAborted(signal);
47+
4648
let sum = 0;
4749
let count = 0;
50+
4851
for await (const item of wrapWithAbort(source, signal)) {
4952
sum += await selector.call(thisArg, item, signal);
5053
count++;

src/asynciterable/catcherror.ts

+7-23
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { AsyncIterableX } from './asynciterablex.js';
2-
import { returnAsyncIterator } from '../util/returniterator.js';
32
import { wrapWithAbort } from './operators/withabort.js';
43
import { throwIfAborted } from '../aborterror.js';
54

@@ -19,29 +18,14 @@ export class CatchAllAsyncIterable<TSource> extends AsyncIterableX<TSource> {
1918
let hasError = false;
2019

2120
for (const source of this._source) {
22-
const it = wrapWithAbort(source, signal)[Symbol.asyncIterator]();
23-
2421
error = null;
2522
hasError = false;
2623

27-
while (1) {
28-
let c = <TSource>{};
29-
30-
try {
31-
const { done, value } = await it.next();
32-
if (done) {
33-
await returnAsyncIterator(it);
34-
break;
35-
}
36-
c = value;
37-
} catch (e) {
38-
error = e;
39-
hasError = true;
40-
await returnAsyncIterator(it);
41-
break;
42-
}
43-
44-
yield c;
24+
try {
25+
yield* wrapWithAbort(source, signal);
26+
} catch (e) {
27+
error = e;
28+
hasError = true;
4529
}
4630

4731
if (!hasError) {
@@ -64,7 +48,7 @@ export class CatchAllAsyncIterable<TSource> extends AsyncIterableX<TSource> {
6448
* sequences until a source sequence terminates successfully.
6549
*/
6650
export function catchAll<T>(source: Iterable<AsyncIterable<T>>): AsyncIterableX<T> {
67-
return new CatchAllAsyncIterable<T>(source);
51+
return new CatchAllAsyncIterable(source);
6852
}
6953

7054
/**
@@ -76,5 +60,5 @@ export function catchAll<T>(source: Iterable<AsyncIterable<T>>): AsyncIterableX<
7660
* sequences until a source sequence terminates successfully.
7761
*/
7862
export function catchError<T>(...args: AsyncIterable<T>[]): AsyncIterableX<T> {
79-
return new CatchAllAsyncIterable<T>(args);
63+
return new CatchAllAsyncIterable(args);
8064
}

src/asynciterable/combinelatest.ts

+33-25
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import { identity } from '../util/identity.js';
33
import { wrapWithAbort } from './operators/withabort.js';
44
import { throwIfAborted } from '../aborterror.js';
55
import { safeRace } from '../util/safeRace.js';
6+
import { returnAsyncIterator } from '../util/returniterator.js';
67

78
// eslint-disable-next-line @typescript-eslint/no-empty-function
8-
const NEVER_PROMISE = new Promise(() => {});
9+
const NEVER_PROMISE = new Promise<never>(() => {});
910

1011
type MergeResult<T> = { value: T; index: number };
1112

@@ -28,39 +29,46 @@ export class CombineLatestAsyncIterable<TSource> extends AsyncIterableX<TSource[
2829
const length = this._sources.length;
2930
const iterators = new Array<AsyncIterator<TSource>>(length);
3031
const nexts = new Array<Promise<MergeResult<IteratorResult<TSource>>>>(length);
31-
let hasValueAll = false;
32-
const values = new Array<TSource>(length);
33-
const hasValues = new Array<boolean>(length);
34-
let active = length;
3532

36-
hasValues.fill(false);
33+
let active = length;
34+
let allValuesAvailable = false;
35+
const values = new Array<TSource>(length);
36+
const hasValues = new Array<boolean>(length).fill(false);
3737

3838
for (let i = 0; i < length; i++) {
3939
const iterator = wrapWithAbort(this._sources[i], signal)[Symbol.asyncIterator]();
4040
iterators[i] = iterator;
4141
nexts[i] = wrapPromiseWithIndex(iterator.next(), i);
4242
}
4343

44-
while (active > 0) {
45-
const next = safeRace(nexts);
46-
const {
47-
value: { value: value$, done: done$ },
48-
index,
49-
} = await next;
50-
if (done$) {
51-
nexts[index] = <Promise<MergeResult<IteratorResult<TSource>>>>NEVER_PROMISE;
52-
active--;
53-
} else {
54-
values[index] = value$;
55-
hasValues[index] = true;
56-
57-
const iterator$ = iterators[index];
58-
nexts[index] = wrapPromiseWithIndex(iterator$.next(), index);
59-
60-
if (hasValueAll || (hasValueAll = hasValues.every(identity))) {
61-
yield values;
44+
try {
45+
while (active > 0) {
46+
const next = safeRace(nexts);
47+
48+
const {
49+
value: { value, done },
50+
index,
51+
} = await next;
52+
53+
if (done) {
54+
nexts[index] = NEVER_PROMISE;
55+
active--;
56+
} else {
57+
values[index] = value;
58+
hasValues[index] = true;
59+
allValuesAvailable = allValuesAvailable || hasValues.every(identity);
60+
61+
nexts[index] = wrapPromiseWithIndex(iterators[index].next(), index);
62+
63+
if (allValuesAvailable) {
64+
yield values;
65+
}
6266
}
6367
}
68+
} finally {
69+
for (const iterator of iterators) {
70+
await returnAsyncIterator(iterator);
71+
}
6472
}
6573
}
6674
}
@@ -176,5 +184,5 @@ export function combineLatest<T, T2, T3, T4, T5, T6>(
176184
*/
177185
export function combineLatest<T>(...sources: AsyncIterable<T>[]): AsyncIterableX<T[]>;
178186
export function combineLatest<T>(...sources: any[]): AsyncIterableX<T[]> {
179-
return new CombineLatestAsyncIterable<T>(sources);
187+
return new CombineLatestAsyncIterable(sources);
180188
}

src/asynciterable/concat.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,17 @@ export class ConcatAsyncIterable<TSource> extends AsyncIterableX<TSource> {
1313

1414
async *[Symbol.asyncIterator](signal?: AbortSignal) {
1515
throwIfAborted(signal);
16+
1617
for (const outer of this._source) {
17-
for await (const item of wrapWithAbort(outer, signal)) {
18-
yield item;
19-
}
18+
yield* wrapWithAbort(outer, signal);
2019
}
2120
}
2221
}
2322

2423
export function _concatAll<TSource>(
2524
source: Iterable<AsyncIterable<TSource>>
2625
): AsyncIterableX<TSource> {
27-
return new ConcatAsyncIterable<TSource>(source);
26+
return new ConcatAsyncIterable(source);
2827
}
2928

3029
/**
@@ -136,5 +135,5 @@ export function concat<T, T2, T3, T4, T5, T6>(
136135
* @returns {AsyncIterableX<T>} An async-iterable sequence that contains the elements of each given sequence, in sequential order.
137136
*/
138137
export function concat<T>(...args: AsyncIterable<T>[]): AsyncIterableX<T> {
139-
return new ConcatAsyncIterable<T>(args);
138+
return new ConcatAsyncIterable(args);
140139
}

src/asynciterable/count.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ export async function count<T>(
1818
): Promise<number> {
1919
const { ['signal']: signal, ['thisArg']: thisArg, ['predicate']: predicate = async () => true } =
2020
options || {};
21+
2122
throwIfAborted(signal);
22-
let i = 0;
2323

24+
let i = 0;
2425
for await (const item of wrapWithAbort(source, signal)) {
2526
if (await predicate.call(thisArg, item, i, signal)) {
2627
i++;

src/asynciterable/create.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ class AnonymousAsyncIterable<T> extends AsyncIterableX<T> {
1111

1212
async *[Symbol.asyncIterator](signal?: AbortSignal) {
1313
throwIfAborted(signal);
14+
1415
const it = await this._fn(signal);
15-
let next: IteratorResult<T> | undefined;
16-
while (!(next = await it.next()).done) {
17-
yield next.value;
18-
}
16+
17+
yield* {
18+
[Symbol.asyncIterator]: () => it,
19+
};
1920
}
2021
}
2122

src/asynciterable/defer.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,8 @@ class DeferAsyncIterable<TSource> extends AsyncIterableX<TSource> {
1414

1515
async *[Symbol.asyncIterator](signal?: AbortSignal) {
1616
throwIfAborted(signal);
17-
const items = await this._fn(signal);
18-
for await (const item of wrapWithAbort(items, signal)) {
19-
yield item;
20-
}
17+
18+
yield* wrapWithAbort(await this._fn(signal), signal);
2119
}
2220
}
2321

@@ -32,5 +30,5 @@ class DeferAsyncIterable<TSource> extends AsyncIterableX<TSource> {
3230
export function defer<TSource>(
3331
factory: (signal?: AbortSignal) => AsyncIterable<TSource> | Promise<AsyncIterable<TSource>>
3432
): AsyncIterableX<TSource> {
35-
return new DeferAsyncIterable<TSource>(factory);
33+
return new DeferAsyncIterable(factory);
3634
}

src/asynciterable/elementat.ts

+2
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@ export async function elementAt<T>(
1717
signal?: AbortSignal
1818
): Promise<T | undefined> {
1919
throwIfAborted(signal);
20+
2021
let i = index;
2122
for await (const item of wrapWithAbort(source, signal)) {
2223
if (i === 0) {
2324
return item;
2425
}
2526
i--;
2627
}
28+
2729
return undefined;
2830
}

src/asynciterable/every.ts

+3
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@ export async function every<T>(
1616
options: FindOptions<T>
1717
): Promise<boolean> {
1818
const { ['signal']: signal, ['thisArg']: thisArg, ['predicate']: predicate } = options;
19+
1920
throwIfAborted(signal);
21+
2022
let i = 0;
2123
for await (const item of wrapWithAbort(source, signal)) {
2224
if (!(await predicate.call(thisArg, item, i++, signal))) {
2325
return false;
2426
}
2527
}
28+
2629
return true;
2730
}

src/asynciterable/find.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ export async function find<T>(
1515
options: FindOptions<T>
1616
): Promise<T | undefined> {
1717
const { ['signal']: signal, ['thisArg']: thisArg, ['predicate']: predicate } = options;
18+
1819
throwIfAborted(signal);
19-
let i = 0;
2020

21+
let i = 0;
2122
for await (const item of wrapWithAbort(source, signal)) {
2223
if (await predicate.call(thisArg, item, i++, signal)) {
2324
return item;
2425
}
2526
}
27+
2628
return undefined;
2729
}

src/asynciterable/findindex.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ export async function findIndex<T>(
1616
options: FindOptions<T>
1717
): Promise<number> {
1818
const { ['signal']: signal, ['thisArg']: thisArg, ['predicate']: predicate } = options;
19+
1920
throwIfAborted(signal);
20-
let i = 0;
2121

22+
let i = 0;
2223
for await (const item of wrapWithAbort(source, signal)) {
2324
if (await predicate.call(thisArg, item, i++, signal)) {
2425
return i;
2526
}
2627
}
28+
2729
return -1;
2830
}

src/asynciterable/first.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ export async function first<T>(
1616
): Promise<T | undefined> {
1717
const { ['signal']: signal, ['thisArg']: thisArg, ['predicate']: predicate = async () => true } =
1818
options || {};
19+
1920
throwIfAborted(signal);
21+
2022
let i = 0;
2123
for await (const item of wrapWithAbort(source, signal)) {
22-
if (await predicate!.call(thisArg, item, i++, signal)) {
24+
if (await predicate.call(thisArg, item, i++, signal)) {
2325
return item;
2426
}
2527
}

0 commit comments

Comments
 (0)