Vize

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.

Open the interactive example

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
Load and recover a team profile: initial state
Initial state
Load and recover a team profile: state after the verified interactions
After the verified interactions

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.