Documentation Index Fetch the complete documentation index at: https://mintlify.com/reatom/reatom/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The withAsync extension adds comprehensive async state management to atoms or actions that return promises. It automatically tracks pending operations, manages errors, and provides lifecycle hooks for handling async events.
This extension preserves Reatom context across async operations, ensuring that async results properly update Reatom state.
Type Signature
function withAsync < Err = Error , EmptyErr = undefined >(
options ?: AsyncOptions < Err , EmptyErr >
) : < T extends AtomLike >(
target : T
) => T extends AtomLike < any , infer Params , Promise < infer Payload >>
? T & AsyncExt < Params , Payload , Err | EmptyErr >
: never
Parameters
Configuration options for async handling Function to transform raw errors into a specific error type Default: Converts to Error instance
Initial/reset value for the error atom Default: undefined
resetError
null | 'onCall' | 'onFulfill'
When to reset the error state
'onCall': Reset error when the async operation starts (default)
'onFulfill': Reset error only when the operation succeeds
null: Never automatically reset errors
Default: 'onCall'Whether to enable the status atom for detailed async operation tracking Default: false
Whether to enable caching of the last called parameters for the retry functionality Default: false
Return Value
Returns the target extended with the following properties:
Computed atom that indicates when no async operations are pending
Computed atom tracking how many async operations are currently pending
Atom containing the most recent error or undefined if no error has occurred
onFulfill
Action<[payload, params], { payload, params }>
Action that is called when the promise resolves successfully
onReject
Action<[error, params], { error, params }>
Action that is called when the promise rejects with an error
Action called after either successful resolution or rejection
retry
Action<[], Promise<Payload>>
Action that retries the last async operation
For atoms: re-evaluates the computed atom
For actions: calls it with the cached params (requires cacheParams: true)
Atom that caches the last called parameters (requires cacheParams: true)
Atom that tracks detailed async operation status (requires status: true)
Examples
Basic Usage with Action
import { action } from '@reatom/core'
import { withAsync } from '@reatom/core/async'
import { wrap } from '@reatom/core/methods'
const fetchUser = action ( async ( userId : string ) => {
const response = await wrap ( fetch ( `/api/users/ ${ userId } ` ))
return await wrap ( response . json ())
}, 'fetchUser' ). extend ( withAsync ())
// Access async state
fetchUser . error () // → latest error if any
fetchUser . ready () // → are all operations complete?
fetchUser . pending () // → number of pending operations
Error Handling
const fetch = action ( async ( shouldFail : boolean ) => {
await wrap ( sleep ())
if ( shouldFail ) throw new Error ( 'Failed!' )
return 'Success'
}, 'fetch' ). extend ( withAsync ())
fetch . onReject . extend (
withCallHook (({ error , params }) => {
console . log ( 'Request failed:' , error . message )
})
)
fetch . onFulfill . extend (
withCallHook (({ payload , params }) => {
console . log ( 'Request succeeded:' , payload )
})
)
await wrap ( fetch ( true ). catch (() => {}))
console . log ( fetch . error ()) // → Error: Failed!
await wrap ( fetch ( false ))
console . log ( fetch . error ()) // → undefined
Retry with Computed
import { atom , computed } from '@reatom/core'
import { withAsync } from '@reatom/core/async'
let shouldFail = true
const params = atom ( 0 , 'params' )
const resource = computed ( async () => {
const value = params ()
if ( shouldFail ) throw new Error ( 'Initial failure' )
return 'Success'
}, 'resource' ). extend ( withAsync ())
// Initial evaluation fails
await wrap ( resource (). catch (() => {}))
console . log ( resource . error ()) // → Error: Initial failure
// Retry after fixing the condition
shouldFail = false
await wrap ( resource . retry ())
console . log ( resource . error ()) // → undefined
Retry with Action (Cached Params)
const fetch = action ( async ( param : number ) => {
await wrap ( sleep ())
return param * 2
}, 'fetch' ). extend ( withAsync ({ cacheParams: true }))
// First call
await wrap ( fetch ( 5 ))
console . log ( fetch . params ()) // → [5]
// Retry uses cached params
await wrap ( fetch . retry ())
console . log ( fetch . params ()) // → [5]
Custom Error Parsing
interface ApiError {
code : string
message : string
}
const fetchData = action ( async () => {
const res = await wrap ( fetch ( '/api/data' ))
if ( ! res . ok ) throw await res . json ()
return res . json ()
}, 'fetchData' ). extend (
withAsync < ApiError >({
parseError : ( error : unknown ) => {
if ( typeof error === 'object' && error !== null ) {
return error as ApiError
}
return { code: 'UNKNOWN' , message: String ( error ) }
},
})
)
await wrap ( fetchData (). catch (() => {}))
const error = fetchData . error ()
if ( error ) {
console . log ( `Error ${ error . code } : ${ error . message } ` )
}
With Status Tracking
const fetchUser = action ( async ( id : string ) => {
const res = await wrap ( fetch ( `/api/users/ ${ id } ` ))
return await wrap ( res . json ())
}, 'fetchUser' ). extend ( withAsync ({ status: true }))
const status = fetchUser . status ()
console . log ( status . isPending ) // → false
console . log ( status . isFirstPending ) // → false
fetchUser ( '123' )
console . log ( fetchUser . status (). isPending ) // → true
console . log ( fetchUser . status (). isFirstPending ) // → true
await wrap ( promise )
console . log ( fetchUser . status (). isFulfilled ) // → true
console . log ( fetchUser . status (). isSettled ) // → true