Future
UpdatedFuture<T>
type Profile = { id: number } async function load(id: number): Profile task.wait(1) return { id = id } end async do local profiles = await Future.all([load(1), load(2)]) local first = await Future.race([load(1), Future.delay(5)]) print(profiles, first) end
A memoized task, from the std: it runs once, settles once, and every await after that reads the same value. An async function returns one and its body runs on task.spawn under xpcall; await yields until it settles, then returns the value or rethrows; try await returns the Err from the enclosing function instead of rethrowing. Both need an async context: see alloy doc await.
An error in a body settles its Future as rejected, and the Future keeps it: nothing prints it. An async do block or an async function call that no code awaits drops its error that way. await the Future, or pass a second callback to and_then, to see the error.
race and any read the value type off each Future in the list, so a mixed list gives the union: Future<number> beside Future<nil> yields Future<number?>. An async function without a return type and without a return value is Future<nil>. A header may name the answer itself, async function f(): Future<T>, or what it settles with, async function f(): T; the two mean one thing.
Statics
Future.resolve
Future.resolve<T>(value: T): Future<T>
A Future that has already settled with value. Every await on it returns at once.
local ready = Future.resolve(42) async do print(await ready) end
Future.reject
Future.reject(error: any): Future<any>
A Future that has already failed with error. An await on it rethrows, and try await returns that Err from the function. The payload reads any: a rejected Future never settles with one, so it stands where a Future<T> is asked for.
local bad = Future.reject("no disk") bad:andThen(nil, function(e) print(e) end)
Future.delay
Future.delay(seconds: number): Future<()>
A Future that settles after the wait, with no value. It is the async form of task.wait.
async do await Future.delay(0.5) print("later") end
Future.all
Future.all<T>(futures: Future<T>[]): Future<T[]>
One Future of every value, in the order of the list. The first failure fails the whole thing, and the rest keep running. Every Future in the list carries one type here; race and any are the ones that take a mixed list.
async do local both = await Future.all([Future.resolve(1), Future.resolve(2)]) print(both:len()) end
Future.race
Future.race<T>(futures: Future<T>[]): Future<T>
The first Future to settle, whether it succeeds or fails. Use it for a timeout beside real work. The futures in the list need not carry one type: the result is the union of their value types, so a value beside a Future.delay is optional.
async do local first = await Future.race([Future.resolve(1), Future.resolve(2)]) print(first) end
Future.any
Future.any<T>(futures: Future<T>[]): Future<T>
The first Future to succeed. It fails only when every one of them fails. As with race, a list whose futures carry different types gives the union of them.
async do local ok = await Future.any([Future.reject("a"), Future.resolve(2)]) print(ok) end
Future.all_settled
Future.all_settled(futures: Future<any>[]): Future<any[]>
One Result per Future, in order. No failure fails it. The checker reads each Result as any, since a list of Results under a Future reaches its limit. Annotate the local to type them: local f: Future<Result<number, any>[]> = Future.all_settled(xs). The error side is any, since a rejection carries whatever it was rejected with.
async do local rs = await Future.all_settled([Future.resolve(1), Future.reject("no")]) print(rs:len()) end
Methods
Future:andThen
Future:andThen(on_resolve: ((T) -> ...any)?, on_reject: ((any) -> ...any)?): Future<T>
Runs a callback when the Future settles and returns the same Future, so calls chain. What the callback returns is dropped: the Future keeps its own value.
local f = Future.resolve(1):andThen(function(n) print(n) end) async do print(await f) end
Future:and_then
Future:and_then(on_resolve: ((T) -> ...any)?, on_reject: ((any) -> ...any)?): Future<T>
The same function under a snake_case name. andThen is the one camelCase name the std carries, so a file that keeps to one spelling writes this.
local f = Future.resolve(1):and_then(function(n) print(n) end) async do print(await f) end
Future:cancel
Future:cancel()
Closes the task behind the Future. An await on a cancelled Future raises.
local slow = Future.delay(10) slow:cancel()
Future:is_settled
Future:is_settled(): boolean
Whether the Future has a value or a failure. A cancelled Future is not settled.
local ready = Future.resolve(1) print(ready:is_settled())
