/
mapAsyncIterator.js
70 lines (64 loc) · 1.97 KB
/
mapAsyncIterator.js
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
import type { PromiseOrValue } from '../jsutils/PromiseOrValue';
/**
* Given an AsyncIterable and a callback function, return an AsyncIterator
* which produces values mapped via calling the callback function.
*/
export function mapAsyncIterator<T, U>(
iterable: AsyncIterable<T> | AsyncGenerator<T, void, void>,
callback: (T) => PromiseOrValue<U>,
rejectCallback: (any) => U = (error) => {
throw error;
},
): AsyncGenerator<U, void, void> {
// $FlowFixMe[prop-missing]
const iteratorMethod = iterable[Symbol.asyncIterator];
const iterator: any = iteratorMethod.call(iterable);
async function abruptClose(error: mixed) {
if (typeof iterator.return === 'function') {
try {
await iterator.return();
} catch (_e) {
/* ignore error */
}
}
throw error;
}
async function mapResult(result: IteratorResult<T, void>) {
if (result.done) {
return result;
}
try {
return { value: await callback(result.value), done: false };
} catch (callbackError) {
return abruptClose(callbackError);
}
}
function mapReject(error: mixed) {
try {
return { value: rejectCallback(error), done: false };
} catch (callbackError) {
return abruptClose(callbackError);
}
}
/* TODO: Flow doesn't support symbols as keys:
https://github.com/facebook/flow/issues/3258 */
return ({
next(): Promise<IteratorResult<U, void>> {
return iterator.next().then(mapResult, mapReject);
},
return() {
return typeof iterator.return === 'function'
? iterator.return().then(mapResult, mapReject)
: Promise.resolve({ value: undefined, done: true });
},
throw(error?: mixed): Promise<IteratorResult<U, void>> {
if (typeof iterator.throw === 'function') {
return iterator.throw(error).then(mapResult, mapReject);
}
return Promise.reject(error).catch(abruptClose);
},
[Symbol.asyncIterator]() {
return this;
},
}: $FlowFixMe);
}