Core API
createAsync(handler, options?)
Creates an AsyncOperation. The handler receives an AsyncContext followed by the parameters passed to execute.
const operation = createAsync(async ({ signal, requestId }, id: string) => loadUser(id, signal), {
concurrency: "latest",
abortable: true,
});
Handler context
| Field | Type | Description |
|---|---|---|
signal | AbortSignal | null | Present when abortable is enabled |
requestId | number | Monotonically increasing operation-local request identifier |
Options
| Option | Default | Description |
|---|---|---|
initialData | null | Data used for the initial and reset snapshots |
dataOnError | Preserve data | Produces replacement data after a rejection |
concurrency | "all" | Selects all-request or latest-request state updates |
abortable | false | Creates an AbortController for every execution |
isEqual | All fields | Determines whether a new snapshot is ignored |
onSuccess | — | Runs after an accepted successful state update |
onError | — | Runs after an accepted error state update |
Operation
| Method | Description |
|---|---|
getSnapshot() | Returns the current frozen snapshot |
subscribe(listener) | Subscribes to accepted snapshot changes and returns an unsubscribe function |
execute(...params) | Starts the handler and returns its promise |
abort() | Aborts controllers, invalidates active work, and returns to idle |
reset() | Invalidates active work and restores initial state |
Error behavior
execute() does not consume errors. It updates the snapshot and invokes onError, then rejects with the original error so the caller decides how to handle it.
try {
await operation.execute();
} catch (error) {
// Handle the same rejection stored in operation.getSnapshot().error.
}
createRequestLifecycle(options?)
This lower-level helper is for adapters that own their state. It tracks request IDs, concurrency,
and optional cancellation without storing data, errors, or loading state. Pass a handler, its
parameter tuple, and start, success, and error callbacks to execute. The settled callbacks
run only when that request is allowed to update state; success and error receive an isLoading
flag. abort() invalidates active requests and returns whether any were active; reset() also
starts a new request generation. The caller must update its own state after aborting or resetting.
const lifecycle = createRequestLifecycle({ concurrency: "latest" });
const state = { data: null, error: null, loading: false };
await lifecycle.execute(async (_context, id: string) => loadUser(id), ["42"], {
start() {
state.loading = true;
},
success(data, isLoading) {
Object.assign(state, { data, error: null, loading: isLoading });
},
error(error, isLoading) {
Object.assign(state, { error, loading: isLoading });
},
});