Eric Bower
·
2025-06-06
race.ts
1import type { Callable, Operation, Task } from "effection";
2import { action, call, resource, spawn } from "effection";
3
4interface OpMap<T = unknown> {
5 [key: string]: Callable<T>;
6}
7
8export function raceMap<T>(opMap: OpMap): Operation<{
9 [K in keyof OpMap<T>]: OpMap[K] extends (...args: any[]) => any
10 ? ReturnType<OpMap[K]>
11 : OpMap[K];
12}> {
13 return resource(function* Race(provide) {
14 const keys = Object.keys(opMap);
15 const taskMap: { [key: string]: Task<unknown> } = {};
16 const resultMap: { [key: keyof OpMap]: OpMap[keyof OpMap] } = {};
17
18 const winner = yield* action<Task<unknown>>(function* (resolve) {
19 for (let i = 0; i < keys.length; i += 1) {
20 const key = keys[i];
21 yield* spawn(function* () {
22 const task = yield* spawn(function* () {
23 yield* call(opMap[key] as any);
24 });
25 taskMap[key] = task;
26 (resultMap as any)[key] = yield* task;
27 resolve(task);
28 });
29 }
30 });
31
32 for (let i = 0; i < keys.length; i += 1) {
33 const key = keys[i];
34 const task = taskMap[key];
35 if (task === winner) {
36 continue;
37 }
38
39 yield* spawn(() => task.halt());
40 }
41
42 yield* provide(resultMap);
43 });
44}