jotai-eager
Version:
Jōtai utilities that help with asynchronous atoms
120 lines (112 loc) • 6.01 kB
TypeScript
import { Atom, ExtractAtomValue, Getter, Setter, WritableAtom } from 'jotai/vanilla';
type AwaitAtomsValues<TTuple extends readonly [Atom<unknown>, ...Atom<unknown>[]]> = {
[Index in keyof TTuple]: Awaited<ExtractAtomValue<TTuple[Index]>>;
};
/**
* Awaits all `deps` if necessary, then runs `op` given all deps in the same order.
* If computing the value fails (throws), a rejected Promise is returned no matter if
* the processing happened synchronously or not.
*
* @deprecated In favor of the eagerAtom() API.
*/
declare function derive<TDeps extends readonly [Atom<unknown>, ...Atom<unknown>[]], TValue>(deps: TDeps, op: (...depValues: AwaitAtomsValues<TDeps>) => TValue): Atom<TValue | Promise<Awaited<TValue>>>;
type AwaitedAll<T extends readonly unknown[]> = {
[K in keyof T]: Awaited<T[K] extends Atom<infer Value> ? Value : T[K]>;
};
interface EagerGetter {
/**
* Retrieves the atom's fulfilled value.
* If the value is not yet available, it interrupts the execution
* of the eager atom until it's available.
*/
<Value>(atom: Atom<Value>): Awaited<Value>;
/**
* Retrieves the Promise's fulfilled value.
* If the value is not yet available, it interrupts the execution
* of the eager atom until it's available.
*/
await<T>(promiseOrValue: T): Awaited<T>;
/**
* Retrieves the fulfilled value of all passed in Promises.
* If the values are not yet available, it interrupts the execution
* of the eager atom until they're available.
*/
awaitAll<T extends readonly unknown[]>(args: T): AwaitedAll<T>;
/**
* Retrieves the fulfilled value of all passed in atoms.
* If the values are not yet available, it interrupts the execution
* of the eager atom until they're available.
*/
all<T extends readonly Atom<unknown>[]>(atoms: T): AwaitedAll<T>;
}
type Read<Value> = (get: EagerGetter) => Value;
type Write<Args extends unknown[], Result> = (get: Getter, set: Setter, ...args: Args) => Result;
type AsyncReadFunctionError = 'ERROR: The `read` function of eager atoms cannot be asynchronous, or return a Promise.';
/**
* A drop-in replacement for vanilla atoms wih custom async read functions, that
* removes unnecessary suspensions. The read function is written as if it was
* synchronous, which allows for:
* - eager computation of the atom's value in case all of its dependencies are fulfilled
* (which is not the case for vanilla async atoms).
* - interrupting computation if a dependency is not yet fulfilled.
*
* @param args A sync read function that can read async atoms directly using the `get` parameter.
* @returns An eager atom
*/
declare function eagerAtom<Value, Args extends unknown[], Result>(...args: [Value] extends [PromiseLike<unknown>] ? [AsyncReadFunctionError] : [read: Read<Value>, write: Write<Args, Result>]): WritableAtom<Promise<Value> | Value, Args, Result>;
declare function eagerAtom<Value>(...args: [Value] extends [PromiseLike<unknown>] ? [AsyncReadFunctionError] : [read: Read<Value>]): Atom<Promise<Value> | Value>;
/**
* Only useful if the eager atom's read function involves a try {} catch {}. Can be used to
* detect whether a thrown value originates from `jotai-eager`, in which case should be rethrown.
* @returns True if `error` is a suspension trigger originating from `jotai-eager`.
*/
declare function isEagerError(error: unknown): boolean;
type Loadable<Value> = {
state: 'loading';
} | {
state: 'hasError';
error: unknown;
} | {
state: 'hasData';
data: Awaited<Value>;
};
declare function loadable<Value>(anAtom: Atom<Value>): Atom<Loadable<Value>>;
/**
* Executes `process` with `data` as input synchronously if `data` is known, meaning
* it is not an unresolved promise of the value.
*
* @param data The data to process, now or later (soon)
* @param process The processing function
* @returns The result (or promise of result) from running `process`.
*/
declare function soon<TInput, TOutput>(data: TInput, process: (knownData: Awaited<TInput>) => TOutput): TOutput | Promise<Awaited<TOutput>>;
/**
* Executes `process` with `data` as input synchronously if `data` is known, meaning
* it is not an unresolved promise of the value.
*
* @param process The processing function
* @returns A function that can be called with `data`, and returns the
* result (or promise of result) from running `process` on `data`.
*/
declare function soon<TInput, TOutput>(process: (knownData: NoInfer<Awaited<TInput>>) => TOutput): (data: TInput) => TOutput | Promise<Awaited<TOutput>>;
type PromiseOrValue<T> = Promise<T> | T;
type SoonAll<T extends readonly unknown[]> = PromiseOrValue<{
[Index in keyof T]: Awaited<T[Index]>;
}>;
/**
* Given array `values`, if all elements are known (are not unresolved promises),
* returns an array of the same length with Awaited `values`. Otherwise, it returns a
* promise to that array.
*/
declare function soonAll<T extends readonly unknown[] | []>(values: T): SoonAll<T>;
declare function soonAll<T extends readonly unknown[]>(values: T): SoonAll<T>;
interface WithPendingContext<Value> {
get: Getter;
prev: Awaited<Value> | undefined;
pending: PromiseLike<Awaited<Value>>;
}
declare function withPending<Value, Args extends unknown[], Result>(anAtom: WritableAtom<Value, Args, Result>): WritableAtom<Awaited<Value> | undefined, Args, Result>;
declare function withPending<Value, Args extends unknown[], Result, PendingValue>(anAtom: WritableAtom<Value, Args, Result>, fallback: (ctx: WithPendingContext<Value>) => PendingValue): WritableAtom<Awaited<Value> | PendingValue, Args, Result>;
declare function withPending<Value>(anAtom: Atom<Value>): Atom<Awaited<Value> | undefined>;
declare function withPending<Value, PendingValue>(anAtom: Atom<Value>, fallback: (ctx: WithPendingContext<Value>) => PendingValue): Atom<Awaited<Value> | PendingValue>;
export { derive, eagerAtom, isEagerError, loadable, soon, soonAll, withPending };