@supercat1337/store2-async
v1.0.0
Published
Reactive async state management for store2 – AsyncAtom, AsyncComputed, cancellation, and reactive helpers
Maintainers
Readme
@supercat1337/store2-async
Reactive async state management for store2
Built‑in cancellation, automatic recomputation, and reactive utilities.
Features
- AsyncAtom – manually controlled reactive asynchronous state with automatic cancellation.
- AsyncComputed – reactive computed state that automatically runs an async function when dependencies change.
- Cancellation – full
AbortSignalsupport usingAbortSignal.anyfor composite signals. - Batched updates – all state changes are batched via
batch/runInActionto minimise notifications. - Utilities –
interval(reactive timer),fromEvent(reactive DOM/EventTarget events),combineSignals. - TypeScript ready – written in JSDoc with bundled type definitions.
- Framework agnostic – works with any framework (React, Vue, Svelte, vanilla JS).
Installation
npm install @supercat1337/store2-asyncPeer dependency:
@supercat1337/store2(v2.0.10 or higher) must be installed.
Quick start
AsyncAtom – manual async state
import { AsyncAtom } from '@supercat1337/store2-async';
import { autorun } from '@supercat1337/store2';
const fetchUser = new AsyncAtom(async (signal, userId) => {
const res = await fetch(`/api/users/${userId}`, { signal });
return res.json();
});
autorun(() => {
if (fetchUser.pending) console.log('Loading…');
if (fetchUser.data) console.log('User:', fetchUser.data);
if (fetchUser.error) console.error('Error:', fetchUser.error);
});
fetchUser.run(123); // starts the request; automatically cancels previous if called againAsyncComputed – auto‑running async computation
import { AsyncComputed } from '@supercat1337/store2-async';
import { atom } from '@supercat1337/store2';
const userId = atom(1);
const userProfile = new AsyncComputed(
() => userId.value, // dependencies tracked here
async (signal, id) => {
const res = await fetch(`/api/users/${id}`, { signal });
return res.json();
},
{ keepPreviousData: true } // keep old data while fetching new
);
// subscribe to changes
userProfile.subscribe(() => {
console.log('Profile:', userProfile.data);
});
userId.value = 2; // automatically triggers a new fetch with cancellationUtilities
interval(ms, initialValue?)
Creates a reactive counter that increments every ms milliseconds.
import { interval } from '@supercat1337/store2-async';
const counter = interval(1000, 5);
autorun(() => console.log(counter.value)); // 5, 6, 7, …
counter.dispose(); // stop the timerfromEvent(target, eventName, transform?)
Subscribes to events on an EventTarget and provides a reactive value.
import { fromEvent } from '@supercat1337/store2-async';
const mouse = fromEvent(document, 'mousemove', e => ({ x: e.clientX, y: e.clientY }), {
debounce: 100,
});
autorun(() => console.log(mouse.value));
mouse.dispose(); // remove listenercombineSignals(...signals)
Combines multiple AbortSignals into one that aborts when any of them aborts.
import { combineSignals } from '@supercat1337/store2-async';
const external = new AbortController();
const combined = combineSignals(external.signal, anotherSignal);
// use combined in fetch / executorAPI Reference
AsyncAtom<T, Args>
- constructor(executor:
(signal: AbortSignal, ...args: Args) => Promise<T>, options?:AsyncAtomOptions<T>) .data– current data (reactive).error– current error (reactive).status–'idle' | 'pending' | 'success' | 'error'.pending,.isSuccess,.isError– computed booleans.run(...args)– executes the async operation, cancels previous run.cancel()– cancels current request, resets status toidle.reset()– cancels and resets to initial state (data →undefinedordefaultValue)
AsyncComputed<T, Deps>
- constructor(depsFn:
() => Deps, asyncFn:(signal: AbortSignal, deps: Deps) => Promise<T>, options?:AsyncComputedOptions<T>) .data,.error,.status,.pending,.isSuccess,.isError– same asAsyncAtom.refetch()– manually trigger a new fetch with current dependencies.cancel()– cancels current request, resets status toidle(keeps data).reset()– cancels and resets todefaultValue.dispose()– removes the reactive reaction and cancels any pending request
Why cancellation resets status to idle?
When .cancel() is called, the request is aborted and the status immediately becomes 'idle'. This is intentional – after cancellation the atom/computed is no longer "loading", and the UI can hide spinners. The data is kept (if any) so you can still show stale content.
License
MIT © Albert Bazaleev
