use-async-state
Reactive state for an async producer.
| Package | @vizejs/composable/use-async-state |
| Own the source | vize lib pull composable:use-async-state |
| Runtime exports | useAsyncState |
| Gzip budget | 768 B |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/composable
Import the entry directly. Call lifecycle-bound utilities inside <script setup> so cleanup follows the component scope; consult the runtime contract for this entry's SSR and hydration behavior.
import { useAsyncState } from "@vizejs/composable/use-async-state";
Try it
Load and recover a team profile
Connect asynchronous sample data to loading, failure, retry, and successful result states without stale results overwriting a newer request.
What to observe: Try the missing profile for an error, then Load Ada to recover. Start Load Ada and immediately Load Grace: Grace finishes first, and Ada's later completion must not replace it.
The producer uses local delayed sample data and sends no network request. immediate: false starts work only after an explicit action; resetOnExecute: true clears the displayed profile while loading. isReady means a request has succeeded at least once. useAsyncState ignores superseded completions; the producer remains responsible for canceling external work, so this SFC clears its own simulation timers on disposal.
Compare the initial and interacted states


Copy the example
Save this complete SFC in your Vue project. Its public imports and source are the same ones executed in the preview.
<script setup lang="ts">
import { onScopeDispose } from "vue";
import { useAsyncState } from "@vizejs/composable/use-async-state";
interface Profile {
name: string;
team: string;
}
const pendingTimers = new Set<ReturnType<typeof setTimeout>>();
async function loadSampleProfile(id: "ada" | "grace" | "missing"): Promise<Profile> {
// Local asynchronous sample data; this example sends no network request.
await new Promise<void>((resolve) => {
const timer = setTimeout(
() => {
pendingTimers.delete(timer);
resolve();
},
id === "grace" ? 100 : 500,
);
pendingTimers.add(timer);
});
if (id === "missing") throw new Error("This profile could not be loaded. Try another profile.");
return id === "ada"
? { name: "Ada Lovelace", team: "Compiler engineering" }
: { name: "Grace Hopper", team: "Developer experience" };
}
onScopeDispose(() => {
for (const timer of pendingTimers) clearTimeout(timer);
pendingTimers.clear();
});
const {
state: profile,
isLoading,
isReady,
error,
execute,
} = useAsyncState(loadSampleProfile, null, {
immediate: false,
resetOnExecute: true,
});
</script>
<template>
<div class="composable-example">
<p>Load sample team profiles with visible loading, error, and recovery states.</p>
<div class="example-actions">
<button type="button" @click="() => execute('ada')">Load Ada</button>
<button type="button" @click="() => execute('grace')">Load Grace</button>
<button type="button" @click="() => execute('missing')">Try missing profile</button>
</div>
<output aria-live="polite">{{
isLoading
? "Loading profile…"
: error instanceof Error
? error.message
: profile !== null
? `Loaded ${profile.name}`
: "Choose a profile to load."
}}</output>
<section v-if="profile !== null" aria-label="Loaded profile">
<strong>{{ profile.name }}</strong>
<p>{{ profile.team }}</p>
</section>
<p>
{{ isReady ? "A profile has been loaded successfully." : "No profile has been loaded yet." }}
</p>
<p>Ada takes 500 ms; Grace takes 100 ms. The newest request owns the result.</p>
</div>
</template>
Runtime contract
| Utility | Category | Stability | SSR | Hydration | Cleanup | Targets | Host globals | Uses |
|---|---|---|---|---|---|---|---|---|
useAsyncState |
async | experimental | safe | stable | none | web, server, worker, native, desktop, terminal | — | — |
API
useAsyncState
Reactive state for an async producer. Tracks the result, loading, readiness, and error of the newest execution; out-of-order completions of older executions are ignored, so the state always reflects the latest request. The typing is arity-aware: producers with parameters require { immediate: false } and execute keeps their parameter tuple. On the server an immediate execution starts but is not awaited, so both sides render the initial state with isLoading: true.
function useAsyncState<Data, Initial>( producer: () => Promise<Data>, initial: Initial, options?: UseAsyncStateOptions<Data>, ): AsyncState<Data, Initial, []>
const { state, isLoading, execute } = useAsyncState((id: string) => fetchUser(id), null, {
immediate: false,
});
await execute("u-1");
useAsyncState
function useAsyncState<Data, Initial, Parameters extends readonly unknown[]>( producer: (...args: Parameters) => Promise<Data>, initial: Initial, options: UseAsyncStateOptions<Data> & { readonly immediate: false }, ): AsyncState<Data, Initial, Parameters>
useAsyncState
function useAsyncState( producer: (...args: readonly unknown[]) => Promise<unknown>, initial: unknown, options: UseAsyncStateOptions<unknown> = {}, ): AsyncState<unknown, unknown, readonly unknown[]>
Types
UseAsyncStateOptions
Options for useAsyncState.
| Member | Type | Description |
|---|---|---|
immediate? |
boolean |
Execute once right away. Only available for producers without parameters; pass false to run a producer that takes arguments through AsyncState.execute. |
resetOnExecute? |
boolean |
Restore the initial state at the start of every execution. |
throwError? |
boolean |
Re-throw failures from execute in addition to storing them. |
onError? |
(error: unknown) => void |
Called with every failure of the newest execution. |
onSuccess? |
(data: Data) => void |
Called with every successful result of the newest execution. |
AsyncState
Reactive state and controls returned by useAsyncState.
| Member | Type | Description |
|---|---|---|
state |
Readonly<ShallowRef<Data | Initial>> |
Newest result, or the initial state. |
isReady |
Readonly<ShallowRef<boolean>> |
Whether at least one execution succeeded. |
isLoading |
Readonly<ShallowRef<boolean>> |
Whether an execution is in flight. |
error |
Readonly<ShallowRef<unknown>> |
Failure of the newest execution, cleared when a new one starts. |
execute |
(...args: Parameters) => Promise<Data | Initial> |
Run the producer. Only the newest execution may update the state. |