The operation receives (input, { signal, id }). Return a value or a promise. Both thrown errors and rejected promises become explicit error results.
Options:
| Option | Default | Meaning |
|---|---|---|
initialData |
undefined |
Data available before the first successful request. |
concurrency |
'latest' |
'latest' supersedes older work; 'single' returns a busy result for overlaps. |
debounceMs |
0 |
Wait for an input pause before executing. Acknowledgment is immediate. |
delayMs |
150 |
Delay before visual pending feedback. Does not delay the request or acknowledgment. |
slowMs |
2500 |
Time until more helpful slow-request feedback. Independent of delayMs. |
timeoutMs |
disabled | Local timeout, reported as TimeoutError. Does not undo server work. |
Timer values must be finite, non-negative numbers no larger than 2,147,483,647. A zero delay enables that feedback immediately. An operation which never settles remains pending until explicitly cancelled, superseded, disposed, or timed out.
With debounce enabled, input and isPending update synchronously and isScheduled is true until the operation starts. Only the latest accepted input executes. Feedback and timeout timers start at execution, excluding the debounce interval. flush() starts scheduled work immediately and is a no-op once it has started. Cancellation, reset, and disposal prevent scheduled work from running. Single concurrency blocks overlap during scheduling as well as execution. retry() also follows the configured debounce policy.
Snapshot
getSnapshot() returns a referentially stable, shallowly frozen snapshot until the next state change:
{
status: 'idle' | 'pending' | 'success' | 'error';
input: Input | undefined;
data: Output | undefined;
error: unknown;
isPending: boolean;
isScheduled: boolean;
showPending: boolean;
isSlow: boolean;
}Data stays available during pending and error states. Treat data and input as immutable; Steady does not deep-clone application objects.
Results
run(input) and retry() resolve to a discriminated result:
type TaskResult<T> =
| { status: 'success'; data: T }
| { status: 'error'; error: unknown }
| { status: 'superseded' | 'cancelled' | 'busy' | 'disposed' | 'idle' };Superseded and cancelled promises settle immediately, even when a transport ignores the signal. Late success and error responses cannot change the current snapshot. retry() reruns the most recently accepted input; before a run or after a reset, it returns idle.
cancel() returns to idle while retaining successful data and the last input. reset() restores the initial snapshot and forgets retry input. dispose() cancels work, clears timers and subscribers, and makes future calls return disposed.
subscribe(listener) returns an unsubscribe function. Listeners are synchronous and should not throw. getInitialSnapshot() returns the stable initial snapshot for server rendering and hydration. Build separate stores per server request; do not share user data in a process-wide singleton.