1import type { Atom, AtomRegistry } from "effect/reactivity";
2
3export async function waitForAtomValue<A>(input: {
4 readonly registry: AtomRegistry.AtomRegistry;
5 readonly atom: Atom.Atom<A>;
6 readonly predicate: (value: A) => boolean;
7 readonly timeoutMs: number;
8}): Promise<boolean> {
9 const read = () => input.registry.get(input.atom);
10 if (input.predicate(read())) {
11 return true;
12 }
13
14 return await new Promise<boolean>((resolve) => {
15 let settled = false;
16 let timeoutId: ReturnType<typeof globalThis.setTimeout> | null = null;
17 let unsubscribe: (() => void) | null = null;
18 let unsubscribeWhenReady = false;
19
20 const finish = (result: boolean) => {
21 if (settled) {
22 return;
23 }
24 settled = true;
25 if (timeoutId !== null) {
26 globalThis.clearTimeout(timeoutId);
27 }
28 if (unsubscribe === null) {
29 unsubscribeWhenReady = true;
30 } else {
31 unsubscribe();
32 }
33 resolve(result);
34 };
35
36 unsubscribe = input.registry.subscribe(input.atom, (value) => {
37 if (input.predicate(value)) {
38 finish(true);
39 }
40 });
41 if (unsubscribeWhenReady) {
42 unsubscribe();
43 return;
44 }
45
46 // Close the gap between the initial read and subscription registration.
47 if (input.predicate(read())) {
48 finish(true);
49 return;
50 }
51
52 timeoutId = globalThis.setTimeout(() => {
53 finish(false);
54 }, input.timeoutMs);
55 });
56}