UNPKG

rvx

Version:

A signal based rendering library

79 lines (70 loc) 2.26 kB
import { $ } from "../core/signals.js"; import type { Component } from "../core/types.js"; import { nest, View } from "../core/view.js"; import { ASYNC } from "./async-context.js"; /** * Render content depending on the state of an async function or promise. * * See {@link Async `<Async>`} when using JSX or when named properties are preferred. * * This task is tracked using the current {@link ASYNC async context} if any. It is guaranteed, that the view is updated before the tracked task completes. */ export function nestAsync<T>(source: (() => Promise<T>) | Promise<T>, component?: Component<T>, pending?: Component, rejected?: Component<unknown>): View { const state = $({ type: 0, value: undefined as unknown }); let promise: Promise<T>; if (typeof source === "function") { promise = (async () => source())(); } else { promise = source; } const ac = ASYNC.current; promise.then(value => { state.value = { type: 1, value }; }, (value: unknown) => { state.value = { type: 2, value }; if (ac === undefined && rejected === undefined) { void Promise.reject(value); } }); ac?.track(promise); return nest(state, state => { switch (state.type) { case 0: return pending?.(); case 1: return component?.(state.value as T); case 2: return rejected?.(state.value); } }); } /** * Render content depending on the state of an async function or promise. * * See {@link nestAsync} when not using JSX or when positional arguments are preferred. * * This task is tracked using the current {@link ASYNC async context} if any. It is guaranteed, that the view is updated before the tracked task completes. */ export function Async<T>(props: { /** * The async function or promise. */ source: (() => Promise<T>) | Promise<T>; /** * A function render content while pending. * * By default, nothing is rendered. */ pending?: () => unknown; /** * A function to render content when resolved. * * By default, nothing is rendered. */ children?: (value: T) => unknown; /** * A function to render content when rejected. * * By default, nothing is rendered. */ rejected?: (value: unknown) => unknown; }): View { return nestAsync(props.source, props.children, props.pending, props.rejected); }