Async
Async values are created with the $async function, which wraps a promise-returning getter. While the promise is pending, reads of the getter suspend (and read as undefined, hence the optional chaining):
export default function Component($props: { id: number }) {
let $state = $watch({
get user() {
return $async(() => fetchUser($props.id))
}
})
@render {
<p>
Hello, {$state.user?.name}!
</p>
}
} Await statements
Use an @await statement to show a with branch while suspended, and the content branch when everything has resolved:
export default function Component($props: { id: number }) {
let $state = $watch({
get user() {
return $async(() => fetchUser($props.id))
}
})
@render {
@await {
<p>
Hello, {$state.user?.name}!
</p>
} with {
<p>Loading...</p>
}
}
} Without a with branch, nothing is rendered while suspended. Once content has rendered, a later suspend keeps the existing content mounted rather than flashing back to the with branch.
The $pending function
The $pending function returns true while any $async getter read within it is loading, which is useful for showing a loading indicator inline:
<button disabled={$pending(() => $state.user)}>
Save
</button>
@if ($pending(() => $state.user)) {
<p>Loading...</p>
} The $refresh function
The $refresh function re-runs the $async getters read within it, without a dependency having changed. Use it for refresh buttons, polling and retry-after-error:
export default function Component() {
let $state = $watch({
get data() {
return $async(() => fetchData())
}
})
function refresh() {
$refresh(() => $state.data)
}
@render {
<button onclick={refresh}>
Refresh
</button>
<p>
{$state.data.message}
</p>
}
} A refresh is loud by default: $pending returns true while it is in flight. Pass { silent: true } for a quiet re-fetch, e.g. for polling in the background.
When an async value throws, the error can be caught with a @try statement. See Errors.
Server rendering
By default $async getters run on the client: the server renders the @await with branch, and fetching starts after the page hydrates. Pass { source: "server" } to fetch during the server render instead — the boundary's content then ships resolved with the page, and the client adopts it directly with no fallback flash:
export default function Component($props: { id: number }) {
let $state = $watch({
get user() {
return $async(() => fetchUser($props.id), {
source: "server",
timeout: 2000, // ms before the server gives up (default 5000)
})
}
})
@render {
@await {
<p>
Hello, {$state.user.name}!
</p>
} with {
<p>Loading...</p>
}
}
} On a timeout (or a rejection, or a client-fetch getter inside the same boundary) the server ships the with branch and the client fetches, exactly like the default. Server-side values must be JSON-safe, and the thunk still runs once on the client in the background so that dependency changes re-fetch — its first result is discarded, so you never see it.