api / const
promstates
Creates promise state signals for async operations.
Creates promise state signals for async operations.
This function returns a tuple containing a promise runner function and three derived signals that track the promise's state: result, error, and running status.
Signature
export const promstates = <R, Args extends Array<any>, I>(
promiseFn: (...args: Args) => Promise<R>,
initialValue?: I,
ultimately?: () => void
): readonly [
/**
* Promise runner method which takes the same arguments as the original promise
* but returns `Promise<void>`.
*/
(...args: Args) => Promise<void>,
/** Derived signal of result of the promise. */
DerivedSignal<unknown extends I ? R | undefined : R | I>,
/** Derived signal of promise error. */
DerivedSignal<Error | undefined>,
/** Derived signal of whether promise is currently running or not */
DerivedSignal<boolean>
] => {
type PromState = {
isRunning: boolean;
result: unknown extends I ? R | undefined : R | I;
error: Error | undefined;
};
const state = signal<PromState>({
isRunning: false,
result: (initialValue || undefined) as unknown extends I
? R | undefined
: R | I,
error: undefined,
});
const runPromise = (...args: Args) =>
promiseFn(...args)
.then((res) => {
state.value = {
isRunning: false,
result: res,
error: undefined,
};
})
.catch((e) => {
const prevResult = state.value.result;
/**
* Result preservation on error:
*
* The result is NOT set to undefined or initialValue when an error occurs.
* This design choice ensures that if the promise is run multiple times and
* fails on the nth run, the result from the (n-1)th successful run is preserved.
*
* Best practice: Always check error first while using promstates.
* Rationale: If the promise fails on a subsequent run, the previous successful
* result remains intact and accessible, while the error signal is updated with
* the current error.
*
* Note: The error signal is always reset to undefined on success. There is no
* value in preserving the error from the last run when a new success occurs.
*/
state.value = {
isRunning: false,
result: prevResult,
error: e,
};
})
.finally(ultimately);
const { isRunning, result, error } = trap(state).props;
return [runPromise, result, error, isRunning] as const;
};
Type Parameters
- The type of the promise result
- The type of the promise function's arguments
- The type of the initial value
Parameters
promiseFn: A promise-returning functioninitialValue: Optional initial value for the result signalultimately: Optional callback to run in the promise's finally block
Returns
A tuple of:
runPromise: Function to run the promiseresult: Derived signal of the promise resulterror: Derived signal of the promise errorisRunning: Derived signal of whether the promise is running
Remarks
- On success:
resultis updated anderroris cleared - On failure:
erroris updated andresultpreserves the previous successful result - The
ultimatelycallback runs in the finally block - The promise can be run multiple times
- If no initial value is provided, the result signal starts as
undefined - If the promise fails multiple times, the last successful result is preserved
Examples
const promiseFn = async (value: number) => value * 2;
const [runPromise, result, error, isRunning] = promstates(promiseFn, 0);
await runPromise(5);
console.log(result.value); // 10
console.log(error.value); // undefined
await runPromise(-1); // Assume this throws
console.log(result.value); // 10 (preserved)
console.log(error.value); // Error instance
// Best practice: always check error first
if (error.value) {
console.error(error.value);
} else {
console.log(result.value);
}
See Also
- DerivedSignal - For the derived signal type