Skip to content

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>
Wraps a function which may yield in a promise. When run, async calls the the function in a coroutine and resolves with the output of the function after any asynchronous actions, and rejects if the function throws an error.

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>{}
Wraps any functions in dictionary with dash.async, returning a new dictionary containing functions that return promises when called rather than yielding.

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


Yieldable

await

function dash.await(value) --> T
Yields completion of a promise 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>
Returns a promise which resolves after the given delayInSeconds.

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>
Returns a promise which completes after the promise input has completed, regardless of whether it has resolved or rejected. The fn is passed 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
Wraps 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
Returns a promise which never resolves or rejects.

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[]>
Given an array of values, this function returns a promise which resolves once all of the array elements have resolved, or rejects if any of the array elements reject.

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{}>
Given a dictionary of values, this function returns a promise which resolves once all of the values in the dictionary have resolved, or rejects if any of them are promises that reject.

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[]>
Returns a promise which completes after the first promise in the array input completes, or first n promises if specified. If any promise rejects, race rejects with the first rejection.

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>
Like dash.resolve but can take any number of arguments.

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>
Try running a function which returns a promise and retry if the function throws and error or the promise rejects. The retry behavior can be adapted using backoffOptions, which can customize the maximum number of retries and the backoff timing of the form [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>
Like dash.compose but takes functions that can return a promise. Returns a promise that resolves once all functions have resolved. Like compose, functions receive the resolution of the previous promise as argument(s).

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>
Resolves to the result of 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 number

timeoutMessage - 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)