Promised one-time watch for changes. Await a reactive condition.
| Stage | Category | Version | Last Updated | Demo |
|---|---|---|---|---|
| 3 | Utilities | 2.0.0-next.2 (next) | Aug 13, 2026 | Demo → |
npm i @solid-primitives/promise@nextA library of reactive primitives and helpers for handling promises.
promiseTimeout— Creates a promise that resolves (or rejects) after given time.raceTimeout— Combination ofPromise.race()andpromiseTimeout.until— Promised one-time watch for changes. Await a reactive condition.untilAll— Likeuntil, but waits for all conditions to be truthy simultaneously.untilAny— Likeuntil, but resolves as soon as any condition becomes truthy.retry— Retries an async function on failure, with optional delay and backoff.changed— A resolver foruntilthat resolves when the source changes N times.
promiseTimeout
Creates a promise that resolves (or rejects) after given time.
How to use it
import { promiseTimeout } from "@solid-primitives/promise";
await promiseTimeout(1000); // resolves after 1 second
try { await promiseTimeout(1000, true, "timeout"); // rejects with 'timeout' after 1 second} catch (e) { console.log(e); // 'timeout'}raceTimeout
Combination of Promise.race() and promiseTimeout.
How to use it
import { raceTimeout } from "@solid-primitives/promise";
await raceTimeout(myPromise, 1000); // resolves after 1 second, or when "myPromise" resolves
try { await raceTimeout(myPromise, 1000, true, "timeout"); // rejects with 'timeout' after 1 second, or resolves when "myPromise" resolves} catch (e) { console.log(e); // 'timeout'}until
Promised one-time watch for changes. Await a reactive condition.
How to use it
It takes a signal or a reactive condition — which will resolve the promise if truthy — as an argument.
Returns a promise that resolves a truthy value of a condition. Or rejects when its root gets disposed.
With a custom reactive condition:
No need for createMemo — the condition is memoized internally.
import { until } from "@solid-primitives/promise";
const [count, setCount] = createSignal(0);
await until(() => count() > 5);With raceTimeout
To limit the maximum time it has for resolving:
import { until, raceTimeout } from "@solid-primitives/promise";
try { const result = await raceTimeout(until(condition), 2000, true, "until was too slow"); // if until is quicker: result; // => truthy condition value} catch (err) { // if timeouts: console.log(err); // => "until was too slow"}Manually stopping computation
If you don't want to use raceTimeout, there are other ways to stop the reactive computation of until if needed.
First, it will stop itself on cleanup.
// the same goes for components as they are roots toocreateRoot(dispose => {
// disposing root causes the promise to reject, // so you need to catch that outcome to prevent errors until(condition) .then(res => {...}) .catch(() => {})
dispose()})Second, using the .dispose() method.
// until returns a promise with a dispose method on itconst promise = until(condition);
// catch the rejection here toopromise.then().catch();
promise.dispose();untilAll
Resolves when all reactive conditions are simultaneously truthy — the reactive equivalent of Promise.all.
Resolves with an array of each condition's truthy value, in the same order as the input. Rejects if the parent owner is disposed before all conditions are met. An empty conditions array resolves immediately with [].
How to use it
import { untilAll } from "@solid-primitives/promise";
const [auth, setAuth] = createSignal(false);const [config, setConfig] = createSignal(false);
// resolves with [true, true] when both signals are truthyawait untilAll([auth, config]);As an async gate inside a createMemo
const report = createMemo(async () => { await untilAll([() => auth.ready(), () => config.loaded()]); return generateReport();});With .dispose() to stop early
const p = untilAll([auth, config]);p.catch(() => {}); // handle the rejection on early disposal
// cancel without waitingp.dispose();untilAny
Resolves when any reactive condition becomes truthy — the reactive equivalent of Promise.any.
Resolves with the first truthy value encountered. Rejects if the parent owner is disposed before any condition is met. An empty conditions array produces a promise that never resolves (mirrors Promise.race([])).
How to use it
import { untilAny } from "@solid-primitives/promise";
const [primary, setPrimary] = createSignal(false);const [fallback, setFallback] = createSignal(false);
const first = await untilAny([primary, fallback]);With custom conditions to identify the winner
const first = await untilAny([ () => (authReady() ? "auth" : false), () => (guestMode() ? "guest" : false),]);// first === "auth" or "guest"retry
Calls an async function up to times attempts, retrying on failure. Optionally waits delay ms between attempts, or uses a function for dynamic (e.g. exponential) backoff.
How to use it
import { retry } from "@solid-primitives/promise";
// basic usage — 3 attempts, no delayconst data = await retry(() => fetch("/api/data").then(r => r.json()));
// exponential backoffconst data = await retry(fetchData, { times: 5, delay: attempt => 100 * 2 ** attempt, shouldRetry: err => err.status !== 401,});Inside a reactive createMemo
const data = createMemo(async () => retry(() => fetch("/api/data").then(r => r.json()), { times: 3, delay: 500 }),);Options
| Option | Type | Default | Description |
|---|---|---|---|
times | number | 3 | Maximum number of attempts |
delay | number | (attempt: number) => number | 0 | Ms to wait between attempts. Pass a function for dynamic backoff. |
shouldRetry | (error: unknown) => boolean | () => true | Return false to stop retrying immediately and rethrow |
changed
A resolver for until that resolves when the source changes a given number of times.
import { until, changed } from "@solid-primitives/promise";
const [count, setCount] = createSignal(0);
// resolves after count changes 3 timesawait until(changed(count, 3));Changelog
See CHANGELOG.md
Inspiration
Original idea for this primitive comes from a VueUse's function of the same name.