Async & Race Conditions
Pulse treats asynchronous operations as a first-class citizen. You don’t need useEffect or complex middleware to handle data fetching or async validation.
Async Guards
Section titled “Async Guards”A Guard evaluator can be an async function.
const isServerOnline = guard("check-server", async () => { const res = await fetch("/health"); if (!res.ok) throw "Server unreachable"; return true;});Pulse automatically manages the lifecycle:
- Pending: When the promise starts.
isServerOnline.pending()istrue. - OK/Fail: When the promise resolves or rejects.
Race Condition Prevention
Section titled “Race Condition Prevention”One of the hardest problems in async UI is Race Conditions. Example: User clicks “Page 1”, then quickly “Page 2”. “Page 1” request finishes after “Page 2” request, overwriting the valid data with stale data.
Pulse solves this automatically.
Pulse assigns an internal runId to every evaluation. If a dependency changes while a previous async evaluation is still pending (e.g., source changed), the previous evaluation is marked as stale.
Even if the stale promise resolves nicely, Pulse ignores its result. This ensures that the state always reflects the latest version of dependencies.
const userId = source(1);
const userData = guard(async () => { const id = userId(); return await fetchUser(id);});
// 1. Set ID to 1 -> Starts Fetch AuserId.set(1);
// 2. Immediately Set ID to 2 -> Starts Fetch BuserId.set(2);
// Fetch A completes. Pulse IGNORES it because ID is already 2.// Fetch B completes. Pulse accepts it.