Quick start

Choose the core package when you do not need a framework adapter.

Install

pnpm add @omni-async/core

Create an operation

import { createAsync } from "@omni-async/core";

const user = createAsync(
  async ({ signal }, id: string) => {
    const response = await fetch(`/api/users/${id}`, { signal: signal ?? undefined });
    if (!response.ok) throw new Error("Unable to load user");
    return response.json() as Promise<{ id: string; name: string }>;
  },
  {
    abortable: true,
    concurrency: "latest",
  },
);

const unsubscribe = user.subscribe(() => {
  console.log(user.getSnapshot());
});

await user.execute("42");
unsubscribe();

The snapshot moves from idle to loading, then to success or error. Calling reset() restores the initial snapshot; calling abort() invalidates active requests.

Use a framework adapter

The adapters provide the same operation as native reactive values. For example, React exposes a trigger and independent state fields:

import { useQuery } from "@omni-async/react";

function UserSearch() {
  const user = useQuery(async (id: string) => {
    const response = await fetch(`/api/users/${id}`);
    if (!response.ok) throw new Error("Unable to load user");
    return response.json() as Promise<{ name: string }>;
  });

  return (
    <button disabled={user.loading} onClick={() => void user.trigger("42")}>
      {user.data?.name ?? "Load user"}
    </button>
  );
}

See framework adapters for Vue and Svelte examples.