-
Notifications
You must be signed in to change notification settings - Fork 0
/
async.ts
176 lines (154 loc) · 7.96 KB
/
async.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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import {
drop,
every,
filter,
FilterFn,
find,
flatMap,
forEach,
ForEachFn,
map,
MapFn,
reduce,
ReduceFn,
some,
take,
toArray
} from "../../operations/async";
import { InputOperationsArray, IterableEngineContext, UnknownReturnedIterableError } from "../../engine/context";
import { isAsyncIterable, isIterable } from "../../async-like";
export interface TC39AsyncIterableHelpersObject<T> extends AsyncIterable<T> {
map?<O>(mapperFn: MapFn<T, O>): TC39AsyncIterableHelpersObject<O>;
filter?(filterFn: FilterFn<T>): TC39AsyncIterableHelpersObject<T>;
take?(limit: number): TC39AsyncIterableHelpersObject<T>;
drop?(limit: number): TC39AsyncIterableHelpersObject<T>;
flatMap?<O>(mapperFn: MapFn<T, Iterable<O>>): TC39AsyncIterableHelpersObject<O>;
reduce?<A>(reduceFn: ReduceFn<T, A>, initial: A): Promise<A>;
toArray?(): Promise<T[]>;
forEach?(callbackFn: ForEachFn<T>): Promise<void>;
some?(filterFn: FilterFn<T>): Promise<boolean>;
every?(filterFn: FilterFn<T>): Promise<boolean>;
find?(filterFn: FilterFn<T>): Promise<T | undefined>;
}
export interface TC39AsyncIteratorHelpersFn<T> {
<O extends InputOperationsArray>(...operations: O): Omit<IterableEngineContext<O, never | number>, "instance"> & { instance(input: AsyncIterable<T> | Iterable<T>): TC39AsyncIterableHelpersObject<T> };
}
export interface AssertTC39AsyncIteratorHelpersFn<O extends InputOperationsArray = InputOperationsArray, T = unknown> {
(test: unknown): asserts test is TC39AsyncIteratorHelpersFn<T>;
}
export async function assertTC39AsyncIteratorHelpersObject<O extends InputOperationsArray, T>(input: unknown): Promise<AssertTC39AsyncIteratorHelpersFn<O, T>> {
let error: unknown;
try {
type HelperFn = <T = unknown, O = unknown, A extends InputOperationsArray<T, O> = InputOperationsArray<T, O>>(...operations: A) => ReturnType<TC39AsyncIteratorHelpersFn<T>>;
function assertFunction(value: unknown): asserts value is HelperFn {
if (typeof value !== "function") throw new Error();
}
assertFunction(input);
const test: HelperFn = input;
function* naturals() {
let i = 0;
while (true) {
yield i;
i += 1;
}
}
const mapNaturalsOps = test(map((value: number) => {
return value * value;
}));
const mapNaturalsResultIterator = mapNaturalsOps.instance(naturals())[Symbol.asyncIterator]();
ok((await mapNaturalsResultIterator.next()).value === 0); // {value: 0, done: false};
ok((await mapNaturalsResultIterator.next()).value === 1); // {value: 1, done: false};
ok((await mapNaturalsResultIterator.next()).value === 4); // {value: 4, done: false};
const filterNaturalsOps = test(filter((value: number) => {
return value % 2 == 0;
}));
const filterNaturalsResultIterator = filterNaturalsOps.instance(naturals())[Symbol.asyncIterator]();
ok((await filterNaturalsResultIterator.next()).value === 0); // {value: 0, done: false};
ok((await filterNaturalsResultIterator.next()).value === 2); // {value: 2, done: false};
ok((await filterNaturalsResultIterator.next()).value === 4); // {value: 4, done: false};
const takeNaturalsOps = test(take(3));
const takeNaturalsResultIterator = takeNaturalsOps.instance(naturals())[Symbol.asyncIterator]();
ok((await takeNaturalsResultIterator.next()).value === 0); // {value: 0, done: false};
ok((await takeNaturalsResultIterator.next()).value === 1); // {value: 1, done: false};
ok((await takeNaturalsResultIterator.next()).value === 2); // {value: 2, done: false};
const takeNaturalsResultDone = await takeNaturalsResultIterator.next(); // {value: undefined, done: true};
ok(takeNaturalsResultDone.done === true);
ok(!takeNaturalsResultDone.value);
const dropNaturalsOps = test(drop(3));
const dropNaturalsResultIterator = dropNaturalsOps.instance(naturals())[Symbol.asyncIterator]();
ok((await dropNaturalsResultIterator.next()).value === 3); // {value: 3, done: false};
ok((await dropNaturalsResultIterator.next()).value === 4); // {value: 4, done: false};
ok((await dropNaturalsResultIterator.next()).value === 5); // {value: 5, done: false};
const flatMapOps = test(flatMap((value: string) => value.split(" ")));
const flatMapResultIterator = flatMapOps.instance(["It's Sunny in", "", "California"])[Symbol.asyncIterator]();
ok((await flatMapResultIterator.next()).value === "It's"); // {value: "It's", done: false};
ok((await flatMapResultIterator.next()).value === "Sunny"); // {value: "Sunny", done: false};
ok((await flatMapResultIterator.next()).value === "in"); // {value: "in", done: false};
ok((await flatMapResultIterator.next()).value === ""); // {value: "", done: false};
ok((await flatMapResultIterator.next()).value === "California"); // {value: "California", done: false};
const flatMapResultDone = await flatMapResultIterator.next(); // {value: undefined, done: true};
ok(flatMapResultDone.done === true);
ok(!flatMapResultDone.value);
const reduceNaturalsOps = test(take(5), reduce((sum: number, value: number) => {
return sum + value;
}, 3));
const reduceNaturalsResultIterable = reduceNaturalsOps.instance(naturals());
const reduceNaturalsResult = await getThrownResult(reduceNaturalsResultIterable);
ok(reduceNaturalsResult === 13);
const naturalsArrayOps = test(take(5), toArray());
// toArray is an Iterable so is seen as a valid operation result
const naturalsArrayResult = await naturalsArrayOps.instance(naturals());
const naturalsArrayResultIterator = isIterable(naturalsArrayResult) ?
naturalsArrayResult[Symbol.iterator]() : naturalsArrayResult[Symbol.asyncIterator]();
const naturalsArrayResult0 = await naturalsArrayResultIterator.next();
ok(naturalsArrayResult0.value === 0);
const naturalsArrayResult1 = await naturalsArrayResultIterator.next();
ok(naturalsArrayResult1.value === 1);
const naturalsArrayResult2 = await naturalsArrayResultIterator.next();
ok(naturalsArrayResult2.value === 2);
const naturalsArrayResult3 = await naturalsArrayResultIterator.next();
ok(naturalsArrayResult3.value === 3);
const naturalsArrayResult4 = await naturalsArrayResultIterator.next();
ok(naturalsArrayResult4.value === 4);
const naturalsArrayResultDone = await naturalsArrayResultIterator.next();
ok(naturalsArrayResultDone.done);
const forEachLog: unknown[] = [];
const forEachOps = test(drop(1), take(3), forEach((value) => forEachLog.push(value)));
await getThrownResult(forEachOps.instance(naturals()));
ok(forEachLog.join(", ") === "1, 2, 3"); // "1, 2, 3"
const someOps = test<number>(take(4), some(v => v > 1));
ok((await getThrownResult(someOps.instance(naturals()))) === true);
const someOpsOneTrue = test(take(4), some(v => v === 1));
ok((await getThrownResult(someOpsOneTrue.instance(naturals()))) === true);
const everyOverOps = test<number>(drop(1), take(4), every(v => v > 1));
ok(await getThrownResult(everyOverOps.instance(naturals())) === false); // false, first value is 1
const everyOverEqualOps = test<number>(drop(1), take(4), every(v => v >= 1));
ok(await getThrownResult(everyOverEqualOps.instance(naturals())) === true);
const findOps = test<number>(find((value: number) => value > 1));
ok(await getThrownResult(findOps.instance(naturals())) === 2);
} catch (caught) {
error = caught;
}
return (inner): asserts inner is TC39AsyncIteratorHelpersFn<T> => {
if (inner !== input) throw new Error("Unexpected value");
if (error) throw error;
};
async function getThrownResult(iterable: unknown) {
if (!isAsyncIterable(iterable)) return iterable;
try {
let value;
for await (value of iterable) { }
return void value;
} catch (error) {
if (error instanceof UnknownReturnedIterableError) {
return error.value;
}
throw error;
}
}
function ok(value: unknown, message?: string): asserts value {
if (!value) {
throw new Error(message);
}
}
}