Suspense¶
Primitives behind PythonNative's async rendering model: the
Suspend signal, the
CoroDriver that steps async def
component bodies synchronously, cached async values
(Resource /
start_resource), and code splitting
with lazy.
The user-facing pieces are the Suspense
boundary component (documented with the other
components) and the
use_resource hook (documented with the
other hooks); this page covers the underlying machinery.
Suspense primitives: suspension signals, resources, and lazy loading.
This module implements the machinery behind PythonNative's async rendering model:
async defcomponents: a component body may be a coroutine. The reconciler drives it synchronously as far as possible (awaits on already-resolved futures complete inline); if it blocks on pending work, the render suspends and the nearestSuspenseboundary shows its fallback until the coroutine finishes.use_resource: a hook that starts an async fetch and caches it across renders. Reading an unresolvedResourcesuspends the render; awaiting it inside anasync defcomponent does the same thing without the boundary having to re-run anything by hand.lazy: code-splitting for components. The loader runs once, and renders suspend until it resolves.
The core building block is CoroDriver,
a miniature task that steps a coroutine synchronously until it
either finishes or blocks on a pending :class:asyncio.Future. This is
what lets an async def component whose awaits are all cached
(resolved resources, completed tasks) render in a single synchronous
pass with zero event-loop round trips, while genuinely pending awaits
suspend cleanly.
Classes:
| Name | Description |
|---|---|
Suspend |
Signal that a render is blocked on pending async work. |
CoroDriver |
Drive a coroutine synchronously until it blocks on pending work. |
Resource |
A cached async value with Suspense integration. |
Functions:
| Name | Description |
|---|---|
start_resource |
Start |
lazy |
Define a component that loads its implementation on first render. |
Suspend
¶
Bases: BaseException
Signal that a render is blocked on pending async work.
Raised while a component renders, either by
Resource.read or by an
async def component body blocking on a pending await. The
reconciler catches it: the nearest
Suspense boundary shows its fallback
(initial mounts), or the component keeps its previous content and
re-renders when the work finishes (updates).
Derives from :class:BaseException so
ErrorBoundary (which catches
:class:Exception) never mistakes a suspension for a crash.
Attributes:
| Name | Type | Description |
|---|---|---|
waitable |
The pending work; exposes |
|
hook_state |
The suspended component's hook state, carried so a Suspense boundary can preserve it across retries (its cached resources survive, so the retry doesn't refetch). |
|
key |
Optional[Tuple[int, Any]]
|
|
label |
Component name for diagnostics. |
CoroDriver
¶
Drive a coroutine synchronously until it blocks on pending work.
A miniature :class:asyncio.Task: it steps the coroutine with
send(), resolving awaits on already-done futures inline. When
the coroutine blocks on a pending future, the driver parks and
resumes from that future's done callback (on the framework loop's
thread). Each step runs inside the :mod:contextvars context
captured at creation, so hooks called after an await still see
the owning component's hook state.
Attributes:
| Name | Type | Description |
|---|---|---|
done |
Whether the coroutine finished (returned, raised, or was cancelled). |
Methods:
| Name | Description |
|---|---|
cancelled |
Whether the driver was cancelled before finishing normally. |
result |
Return the coroutine's return value (raises if not done or failed). |
exception |
Return the coroutine's exception, or |
add_done_callback |
Invoke |
start |
Run the coroutine as far as it can go without blocking. |
cancel |
Throw :class: |
Resource
¶
Resource(driver: CoroDriver)
Bases: Generic[T]
A cached async value with Suspense integration.
Returned by use_resource. A resource
starts fetching as soon as the hook runs and remembers its result
across renders (until its dependencies change), so re-renders never
refetch.
Two ways to consume it:
resource.read()in a regular component: returns the value when ready, re-raises the fetcher's error if it failed, and suspends the render while pending.await resourcein anasync defcomponent: same semantics, expressed as a plain await.
Methods:
| Name | Description |
|---|---|
read |
Return the fetched value, or suspend the render while pending. |
cancel |
Cancel the in-flight fetch (no-op when already done). |
Attributes:
| Name | Type | Description |
|---|---|---|
ready |
bool
|
Whether the fetch has finished (successfully or not). |
read
¶
Return the fetched value, or suspend the render while pending.
Raises:
| Type | Description |
|---|---|
Suspend
|
While the fetch is still in flight (caught by the reconciler, never by user code). |
BaseException
|
Whatever the fetcher raised, re-raised so an
enclosing |
start_resource
¶
Start fetcher immediately and wrap it in a Resource.
The fetcher may be an async def (typical) or a plain function;
synchronous results resolve the resource immediately, so reading
it never suspends.
This is the non-hook constructor used for module-level resources
(preloading data before a screen mounts) and by
lazy. Inside components, prefer
use_resource, which caches per
component instance and re-fetches when dependencies change.
lazy
¶
Define a component that loads its implementation on first render.
loader runs once, the first time the returned component
renders; until it resolves, renders suspend (so wrap usages in a
Suspense boundary to show a loading
state). The loaded value must be a component (a @component
function or any element factory).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
loader
|
Callable[[], Any]
|
A zero-arg callable returning the component, or an
|
required |
Returns:
| Type | Description |
|---|---|
Callable[..., Any]
|
A component. Props (and children) pass through to the loaded |
Callable[..., Any]
|
component unchanged. |
Next steps¶
- Walk through async components,
Suspense, anduse_resourceend-to-end: Async + data guide. - See how suspension threads through the render pass: Architecture.