Wait for a selector to reach an expected value before continuing.
waitFor is a saga utility that pauses execution until a selector's value matches an expected condition. It's useful for coordinating async operations that depend on state changes — for example, waiting for data to finish loading before proceeding.
Import: import { waitFor } from "@augmentcode/themis/saga" (the public @augmentcode/themis/saga subpackage; the internal implementation lives in src/slices/store-utility/sagas/waitFor.ts and must not be imported directly)
function* waitFor<ARGS extends any[], R>(
selector: StoreSelector<R, ARGS>,
args: ARGS,
isExpectedValue: (value: R, prevVal: R | undefined | null) => boolean,
timeoutMs?: number
): Generator<any, boolean, any>| Parameter | Type | Description |
|---|---|---|
| selector | StoreSelector<R, ARGS> | A named selector; for production app-local selectors, create it with store.createSelector(...) |
| args | ARGS | Stable arguments to pass to the selector (use [] for no-arg selectors) |
| isExpectedValue | (value: R, prevVal: R | undefined |
| timeoutMs | number (optional) | Maximum wait time in milliseconds |
- Returns
truewhen the expected condition is met - Returns
falseiftimeoutMsis provided and the timeout elapses first - Without
timeoutMs, waits indefinitely until the condition is met
- Immediate check — Reads the selector's current value. If it already matches, returns
trueimmediately. - Channel subscription — If the value doesn't match, creates a channel to listen for state changes via
createChannelFromSelector. - Value monitoring — Each time the selector emits a new value, checks the predicate.
- Timeout race — If
timeoutMsis provided, races between value match and adelay(timeoutMs). - Cleanup — Always closes the channel in a
finallyblock to prevent memory leaks.
waitFor(selector, args, predicate, timeout?)
│
├─ Read current value via selector.effect()
│ └─ If predicate(value) → return true (immediate)
│
├─ Create channel from selector
│
├─ If timeout provided:
│ └─ race({ value: checkChannel, timeout: delay(ms) })
│ ├─ value wins → return true
│ └─ timeout wins → return false
│
└─ If no timeout:
└─ Wait on channel until predicate matches → return true
import { waitFor } from "@augmentcode/themis/saga";
function* mySaga() {
// Wait for loading to complete
yield* waitFor(
selectIsLoading,
[],
(isLoading) => isLoading === false
);
// Continue after loading is done
yield* put(doNextStep());
}function* processItem(itemId: string) {
// Wait for a specific item's status
yield* waitFor(
selectItemStatus,
[itemId],
(status) => status === "ready"
);
yield* put(startProcessing(itemId));
}Use the same selector argument stability policy as .effect(...) and selector
channels: prefer primitive scalar args in the tuple, and avoid fresh object,
array, or function literals. Object/function args are valid only when the
selector intentionally keys by a stable reference, such as a memoized config or
existing source object.
The predicate receives both the current and previous value, enabling change detection:
function* detectChange() {
// Wait for value to change from its current state
yield* waitFor(
selectStatus,
[],
(value, prevVal) => {
return prevVal !== undefined && value !== prevVal;
}
);
yield* put(statusChanged());
}function* waitForCompletion(taskId: string) {
yield* waitFor(
selectTaskPhase,
[taskId],
(phase) => phase === "completed" || phase === "error"
);
}- Always use named selectors — Pass named selectors; for production app-local selectors, create them with
store.createSelector(...), never inline lambdas. - Keep
isExpectedValuepure — No side effects, no API calls, no mutations. - Add timeouts for production code — Prevent indefinite waits.
- Handle the timeout case — When using
timeoutMs, always check the return value. - Use empty array for no-arg selectors —
waitFor(selectFoo, [], predicate). - Keep selector args stable — Prefer scalar args; pass object/function args only when their identity is stable and intentional.
- Don't use inline selectors —
waitFor((state) => state.foo, ...)is not supported. - Don't perform side effects in the predicate — Keep
isExpectedValuepure. - Don't wait indefinitely in production — Always consider a reasonable timeout.
- Don't use for polling — Use
takeLatestFromSelectoror channels for continuous monitoring. - Don't ignore the return value when using timeouts — A
falsereturn means the condition was not met.
| Utility | Purpose |
|---|---|
| createChannelFromSelector | Create a channel from a selector for custom patterns |
| takeLatestFromSelector | Watch selector changes continuously (cancel previous) |
| takeEveryFromSelector | Watch selector changes continuously (handle all) |
| delay | Simple time-based delays without state monitoring |
→ See SAGAS.md for channel and selector effect patterns.