Home › Concepts
Async
Flux.async runs yielding work (DataStore requests, HTTP fetches, Players:GetNameFromUserIdAsync) off the reactive graph and exposes its progress as four reactive nodes. If inputs change while a request is in flight, it automatically discards stale responses.
local currentUserId = Flux.signal(1)
local profile = Flux.async(currentUserId, function(userId)
return Players:GetNameFromUserIdAsync(userId)
end)
local label = Flux.new "TextLabel" {
Text = function()
if profile.loading() then
return "Loading..."
elseif profile.error() then
return `Error: {profile.error()}`
end
return `Player: {profile.data() or "None"}`
end,
}Creating an Async Node
Call Flux.async(source, fetcher, initialValue?) (or Flux.async(fetcher, initialValue?) for a one-shot fetch with no reactive source):
source(optional): A reactive Node or() -> Sgetter. Wheneversourcechanges to a truthy value,fetcherruns on a spawned thread. Whilesourceisnilorfalse, the fetch doesn't run.fetcher(value, previous, refetching): The yielding callback. It receivessource's currentvalue, thepreviousresolved.data, andrefetching(falseon automatic runs, or the argument passed to:refetch(info)). The fetcher runs untracked, so pass any reactive inputs throughsource.initialValue(optional): Starting value for.databefore the first fetch resolves.
-- Multiple inputs: combine them in a getter source
local leaderboard = Flux.async(
function()
return { region = region(), page = page() }
end,
function(query)
return fetchPageAsync(query.region, query.page)
end
)Reading Async State
An Async<T> resource exposes four reactive Nodes:
| Node | Type | Description |
|---|---|---|
async.data | Node<T?> | Latest resolved value (or initialValue before first resolution) |
async.loading | Node<boolean> | true while a fetch is in flight |
async.error | Node<string?> | Error string (tostring(err)) if the last fetch threw, else nil |
async.state | Node<AsyncState> | "unresolved" | "pending" | "refreshing" | "ready" | "errored" |
You can pass async.state to Flux.switch to mount separate views for "pending", "errored", and "ready" states.
Automatic Race Condition Handling
Each fetch increments an internal request ID. If source changes while an older request is still yielding, only the latest request updates .data or .error when it finishes. Outdated responses are ignored.
Refetching and Mutating
async:refetch(info?)re-runsfetcherwith the currentsourcevalue without writing tosource.infois passed as the third argument (refetching) tofetcher(defaulting totrue).async:mutate(value)writesvaluedirectly to.data, clears.errorand.loading, and invalidates any in-flight request so a slow response cannot overwrite the optimistic update.
local retryButton = Flux.new "TextButton" {
Text = "Retry",
Activated = function()
profile:refetch()
end,
}Cleaning Up
Created inside a Flux.scope, an Async resource is destroyed automatically with the scope. To tear down an unowned resource manually, call async:Destroy(), which cancels any in-flight fetch and disposes its internal nodes.