-
Notifications
You must be signed in to change notification settings - Fork 2k
/
Copy pathmapAsyncIterable.ts
75 lines (70 loc) · 2.01 KB
/
mapAsyncIterable.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import type { PromiseOrValue } from '../jsutils/PromiseOrValue.js';
/**
* Given an AsyncIterable and a onValue function, return an AsyncIterator
* which produces values mapped via calling the onValue function.
*/
export function mapAsyncIterable<T, U, R = undefined>(
iterable: AsyncGenerator<T, R, void> | AsyncIterable<T>,
onValue: (value: T) => PromiseOrValue<U>,
onError: (error: any) => PromiseOrValue<U> = (error: any) => {
throw error;
},
onDone?: (() => void) | undefined,
): AsyncGenerator<U, R, void> {
const iterator = iterable[Symbol.asyncIterator]();
let errored = false;
async function mapResult(
promise: Promise<IteratorResult<T, R>>,
): Promise<IteratorResult<U, R>> {
let value: T;
try {
const result = await promise;
if (result.done) {
onDone?.();
return result;
}
value = result.value;
} catch (error) {
errored = true;
onDone?.();
return { value: await onError(error), done: false };
}
try {
return { value: await onValue(value), done: false };
} catch (error) {
/* c8 ignore start */
// FIXME: add test case
if (typeof iterator.return === 'function') {
try {
await iterator.return();
} catch (_e) {
/* ignore error */
}
}
throw error;
/* c8 ignore stop */
}
}
return {
async next() {
return errored
? Promise.resolve({ value: undefined as any, done: true })
: mapResult(iterator.next());
},
async return(): Promise<IteratorResult<U, R>> {
// If iterator.return() does not exist, then type R must be undefined.
return typeof iterator.return === 'function'
? mapResult(iterator.return())
: { value: undefined as any, done: true };
},
async throw(error?: unknown) {
if (typeof iterator.throw === 'function') {
return mapResult(iterator.throw(error));
}
throw error;
},
[Symbol.asyncIterator]() {
return this;
},
};
}