Skip to content

[Feature]: Client async-concurrency primitives — Suspense / transition / deferred-value #135

Description

@JosunLP

Pre-flight checklist

  • I searched existing issues and did not find a duplicate request.
  • I explained the problem or use case, not just the desired API.

Problem or use case
bQuery has worker-based concurrency and SSR defer, and async data lives in reactive (useAsyncData/useFetch), but there is no UI-level concurrency model for keeping the interface responsive during async work: no Suspense-equivalent for declarative loading boundaries, no transition primitive to mark non-urgent updates, and no deferred-value primitive to debounce expensive derived UI. React 19 (<Suspense>, useTransition, useDeferredValue) and Svelte 5 (await + $effect.pending()) both ship these, and their absence is a visible capability gap for interactive apps.

Proposed solution
Add client concurrency primitives that build on existing signals: a declarative async boundary (Suspense-equivalent) that shows fallback UI while tracked async work is pending, a transition primitive that flags updates as non-urgent so input stays responsive, and a deferred signal wrapper that lags behind its source to throttle expensive recomputation. Keep them in or adjacent to concurrency/reactive, zero-dependency and tree-shakeable.

Possible API or UX shape

import { suspense, startTransition, deferred } from "@bquery/bquery/concurrency";

// declarative async boundary in the view layer
// <div bq-suspense="userResource" bq-fallback="Loading…">…</div>

const [isPending, start] = startTransition();
start(() => filter.value = nextQuery); // non-urgent, keeps input snappy

const deferredQuery = deferred(query); // lags source; drives expensive list

Alternatives considered
Telling users to debounce manually and roll their own loading flags — works but is boilerplate every app reinvents, and has no parity with the ergonomic primitives competitors ship. Folding this into the worker concurrency API — different concern; worker concurrency is about offloading CPU, this is about UI scheduling, so they should be distinct primitives even if co-located.

Relevant area
concurrency

Additional context
This closes one of the clearest React-parity gaps. Pairs with SSR suspense streaming (renderToStreamSuspense/defer) so the same async boundaries can stream on the server and suspend on the client.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions