Chaining
Then, Catch, and Finally let you compose async steps without nested callbacks. Each method returns the same Promise, so you can chain them in sequence.
Then
Called when the Promise resolves. The return value of fn becomes the resolved value of the next step in the chain. If fn returns a Promise, the chain waits for that Promise to settle before continuing.
network:GetAsync("shop/catalogue")
:Then(function(res)
return res.data.items -- pass items to the next Then
end)
:Then(function(items)
populateShop(items) -- receives the return value from above
end)
Catch
Called when any step in the chain rejects (errors or times out). A single :Catch() at the end of the chain handles errors from all preceding steps — you don't need one after every :Then().
network:GetAsync("player/data")
:Then(function(res) UI:Load(res.data) end)
:Then(function() UI:Show() end)
:Catch(function(err)
-- catches errors from any Then above
warn(err.message)
UI:ShowError("Failed to load player data")
end)
Finally
Called regardless of whether the Promise resolved or rejected. Use it for cleanup that must always run — hiding a loading spinner, unlocking a button, etc. The return value of fn is ignored.
LoadingSpinner.Visible = true
network:GetAsync("leaderboard")
:Then(function(res) LeaderboardUI:Populate(res.data) end)
:Catch(function(err) warn(err.message) end)
:Finally(function()
LoadingSpinner.Visible = false -- always runs
end)
How errors propagate
When a step rejects — either because the network returned an error/timeout, or because a :Then() callback threw — the rejection skips all subsequent :Then() steps and jumps to the first :Catch() in the chain. After :Catch() handles the error, the chain continues normally (resolved) unless :Catch() itself throws.
network:GetAsync("missing/route")
:Then(function(res)
-- SKIPPED if the request failed
UI:Load(res.data)
end)
:Then(function()
-- also SKIPPED
UI:Show()
end)
:Catch(function(err)
-- runs here with the NetworkResponse that has type == "error"
warn("Error:", err.type, err.message)
end)
:Finally(function()
-- ALWAYS runs
LoadingSpinner.Visible = false
end)
Sequential requests
Return a Promise from inside :Then() to chain network calls in order. The outer chain does not advance until the returned Promise settles.
network:GetAsync("shop/catalogue")
:Then(function(res)
ShopUI:Load(res.data)
return network:GetAsync("player/data") -- wait for this too
end)
:Then(function(res)
CoinsUI:Set(res.data.coins) -- player/data result
end)
:Catch(function(err)
UI:ShowError(err.message)
end)
See also
← Promise · Creation | what resolves and rejects · Async / Promise API | methods that return Promises · Promise Guide | parallel merge and advanced patterns