Async¶
Building upon the functionality of Roblox Lua Promise and borrowing ideas from Bluebird, these functions improve the experience of working with asynchronous code in Roblox.
Promises can be thought of as a variable whose value might not be known immediately when they are defined. They allow you to pass around a "promise" to the value, rather than yielding or waiting until the value has resolved. This means you can write functions which pass any promises to right places in your code, and delay running any code which requires the value until it is ready.
Functions¶
async¶
function dash.async(fn) --> (...A) -> Promise<T>
Type
<T, A>(Yieldable<T, A>) -> ...A -> Promise<T>
Generics
T -
any
- the primary type (extends any value)A -
any
- the primary arguments (extends any value)
Parameters
fn -
Yieldable<T, A>
- a Yieldable (of the primary type and the primary arguments)
Returns
(...A) -> Promise<T>
- a function (taking the primary arguments, and returning a Promise (of the primary type))
Rejects
- passthrough - The returned promise will reject if promises passed as arguments reject.
Examples
local fetch = dash.async(function(url) local HttpService = game:GetService("HttpService") return HttpService:GetAsync(url) end) fetch("http://example.com/burger"):andThen(function(meal) print("Meal:", meal) end) -->> Meal: Cheeseburger (ideal response)
Usage
- With
promise:await
the dash.async function can be used just like the async-await pattern in languages like JS.
See
asyncAll¶
function dash.asyncAll(dictionary) --> (...A) -> Promise<T>{}
Type
<T, A>(Yieldable<T, A>{}) -> (...A -> Promise<T>){}
Generics
T -
any
- the primary type (extends any value)A -
any
- the primary arguments (extends any value)
Parameters
dictionary -
Yieldable<T, A>{}
- a dictionary (of Yieldables (of the primary type and the primary arguments))
Returns
(...A) -> Promise<T>{}
- a dictionary (of functions (taking the primary arguments, and returning a Promise (of the primary type)))
Examples
local http = dash.asyncAll(game:GetService("HttpService")) http:GetAsync("http://example.com/burger"):andThen(function(meal) print("Meal", meal) end) -->> Meal: Cheeseburger (some time later)
local buyDinner = dash.async(function() local http = dash.asyncAll(game:GetService("HttpService")) local order = dash.parallelAll({ main = http:GetAsync("http://example.com/burger"), side = http:GetAsync("http://example.com/fries") }) return http:PostAsync("http://example.com/purchase", order:await()) end) buyDinner():await() --> "Purchased!" (some time later)
See
await¶
function dash.await(value) --> T
promise:await()
, but returns immediately with the value if it
isn't a promise.
Type
<T>(Promise<T> | T -> yield T)
Generics
T -
any
- the primary type (extends any value)
Parameters
value -
Promise<T> | T
- a Promise (of the primary type) or the primary type
Returns
T
- the primary type
Examples
local heat = function(item) return dash.delay(1).returns("hot " .. item) end local recipe = {"wrap", heat("steak"), heat("rice")} local burrito = dash.map(recipe, dash.await) dash.debug("{:#?}", burrito) -->> {"wrap", "hot steak", "hot rice"} (2 seconds)
delay¶
function dash.delay(delayInSeconds) --> Promise<nil>
Type
number -> Promise<nil>
Parameters
delayInSeconds -
number
- a number
Returns
Promise<nil>
- a Promise (of nil)
Examples
dash.delay(1):andThen(function() print("Delivered") end) -->> Delivered (1 second later)
finally¶
function dash.finally(promise, fn) --> Promise<R>
true
if the promise did not error,
otherwise false
, and the promise's result as the second argument.
Type
<T, R>(Promise<T>, (bool, T) -> R) -> Promise<R>
Generics
T -
any
- the primary type (extends any value)R -
any
- the result type (extends any value)
Parameters
promise -
(Promise<T>, bool, T) -> R
- a function (taking a Promise (of the primary type) and a tuple (a boolean and the primary type), and returning the result type)fn -
any
- any value - function(ok, result)
Returns
Promise<R>
- a Promise (of the result type)
Examples
local getHunger = dash.async(function(player) if player.health == 0 then error("Player is dead!") else return game.ReplicatedStorage.GetHunger:InvokeServer( player ) end end) local localPlayer = game.Players.LocalPlayer local isHungry = getHunger( localPlayer ):finally(function(isAlive, result) return isAlive and result < 5 end)
isPromise¶
function dash.isPromise(value) --> bool
Promise.is
but catches any errors thrown in attempting to ascertain if value is a
promise, which will occur if the value throws when trying to access missing keys.
Type
<T>(T -> bool)
Generics
T -
any
- the primary type (extends any value)
Parameters
value -
T
- the primary type
Returns
bool
- a boolean
never¶
function dash.never() --> never
Type
() -> never
Returns
never
- a promise that never resolves
Usage
- Useful in combination with dash.race where a resolution or rejection should be ignored.
parallel¶
function dash.parallel(array) --> Promise<T[]>
Type
<T>((Promise<T> | T)[] -> Promise<T[]>)
Generics
T -
any
- the primary type (extends any value)
Parameters
array -
Promise<T> | T[]
- an array (of Promises (of the primary type) or the primary type)
Returns
Promise<T[]>
- a Promise (of an array (of the primary type)) - an array mapping the input to resolved elements.
Rejects
- passthrough - The returned promise will reject if promises passed as arguments reject.
Examples
local heat = function(item) local oven = dash.parallel({item, dash.delay(1)}) return oven:andThen(function(result) return "hot-" .. result[1] end) end local meal =dash.parallel({heat("cheese"), "tomato"}) meal:await() --> {"hot-cheese", "tomato"} (1 second later)
Usage
-
This function is like dash.all but allows objects in the array which aren't promises. These are considered resolved immediately.
-
Promises that return nil values will cause the return array to be sparse.
See
parallelAll¶
function dash.parallelAll(dictionary) --> Promise<T{}>
Type
<T>((Promise<T> | T){}) -> Promise<T{}>
Generics
T -
any
- the primary type (extends any value)
Parameters
dictionary -
Promise<T> | T{}
- a dictionary (of Promises (of the primary type) or the primary type)
Returns
Promise<T{}>
- a Promise (of a dictionary (of the primary type)) - a dictionary mapping the input to resolved elements.
Rejects
- passthrough - The returned promise will reject if promises passed as arguments reject.
Examples
local heat = function(item) local oven = dash.parallel({item, dash.delay(1)}) return oven:andThen(function(result) return "hot-" .. result[1] end) end local toastie = dash.parallelAll({ bread = "brown", filling = heat("cheese") }) toastie:await() --> {bread = "brown", filling = "hot-cheese"} (1 second later)
local fetch = dash.async(function(url) local HttpService = game:GetService("HttpService") return HttpService:GetAsync(url) end) dash.parallelAll({ main = fetch("http://example.com/burger"), side = fetch("http://example.com/fries") }):andThen(function(meal) print("Meal", dash.pretty(meal)) end)
Usage
- Values which are not promises are considered resolved immediately.
race¶
function dash.race(array, n) --> Promise<T[]>
Type
<T>(Promise<T>[], uint?) -> Promise<T[]>
Generics
T -
any
- the primary type (extends any value)
Parameters
array -
Promise<T>[]
- an array (of Promises (of the primary type))n -
uint?
- an unsigned integer (optional) - the number of promises required (default = 1)
Returns
Promise<T[]>
- a Promise (of an array (of the primary type)) - an array containing the first n resolutions, in the order that they resolved.
Throws
- OutOfBoundsError - if the number of required promises is greater than the input length.
Rejects
- passthrough - The returned promise will reject if promises passed as arguments reject.
Examples
-- Here promise resolves to the result of fetch, or resolves to "No burger for you" if the -- fetch takes more than 2 seconds. local fetch = dash.async(function(url) local HttpService = game:GetService("HttpService") return HttpService:GetAsync(url) end) local promise = dash.race( dash.delay(2):andThen(dash.returns("No burger for you"), fetch("http://example.com/burger") )
Usage
-
Note that Promises which return nil values will produce a sparse array.
-
The size of array must be equal to or larger than n.
See
resolve¶
function dash.resolve(...) --> Promise<T>
Type
T -> Promise<T>
Parameters
... -
T
- the type of self
Returns
Promise<T>
- a Promise (of the type of self)
Examples
local function mash( veg ) return dash.resolve("mashed", veg) end mash("potato"):andThen(function(style, veg) dash.debug("{} was {}", veg, style) end) -- >> potato was mashed
Usage
- As
dash.resolve(promise) --> promise
, this function can also be used to ensure a value is a promise.
retryWithBackoff¶
function dash.retryWithBackoff(asyncFn, backoffOptions) --> Promise<T>
[0, x^attemptNumber] + y
where x is an exponent that produces
a random exponential delay and y is a constant delay.
Type
<T>(Async<T>, BackoffOptions<T>) -> Promise<T>
Generics
T -
any
- the primary type (extends any value)
Parameters
asyncFn -
Async<T>
- an Async (of the primary type)backoffOptions -
BackoffOptions<T>
- a BackoffOptions (of the primary type)
Returns
Promise<T>
- a Promise (of the primary type)
Rejects
- passthrough - The returned promise will reject if promises passed as arguments reject.
Examples
-- Use dash.retryWithBackoff to retry a GET request repeatedly. local fetchPizza = dash.async(function() local HttpService = game:GetService("HttpService") return HttpService:GetAsync("https://example.com/pizza") end) dash.retryWithBackoff(fetchPizza, { maxTries = 3, onRetry = function(waitTime, errorMessage) print("Failed to fetch due to", errorMessage) print("Retrying in ", waitTime) end }):andThen(function(resultingPizza) print("Great, you have: ", resultingPizza) end)
series¶
function dash.series(...) --> (...A) -> Promise<A>
Type
<A>((...A -> Promise<A>)[]) -> ...A -> Promise<A>
Generics
A -
any
- the primary arguments (extends any value)
Parameters
... -
(...A) -> Promise<A>[]
- an array (of functions (taking the primary arguments, and returning a Promise (of the primary arguments)))
Returns
(...A) -> Promise<A>
- a function (taking the primary arguments, and returning a Promise (of the primary arguments))
Examples
local function fry(item) return dash.delay(1):andThen(dash.returns("fried " .. item)) end local function cheesify(item) return dash.delay(1):andThen(dash.returns("cheesy " .. item)) end local prepare = dash.series(fry, cheesify) prepare("nachos"):await() --> "cheesy fried nachos" (after 2s)
See
timeout¶
function dash.timeout(promise, deadlineInSeconds, timeoutMessage) --> Promise<T>
promise
if it resolves before the deadline, otherwise rejects with
an error, which can be optionally customized.
Type
<T>(Promise<T>, number, string?) -> Promise<T>
Generics
T -
any
- the primary type (extends any value)
Parameters
promise -
Promise<T>
- a Promise (of the primary type)deadlineInSeconds -
number
- a numbertimeoutMessage -
string?
- a string (optional) - (default = "TimeoutError")
Returns
Promise<T>
- a Promise (of the primary type)
Rejects
- TimeoutError - or timeoutMessage
Examples
let eatGreens = function() return dash.never end dash.timeout(eatGreens(), 10, "TasteError"):await() --> throws "TasteError" (after 10s)