std/async
Run async functions, wait on time, and combine awaitables.
import "std/async"
import "std/io"
fetch :: fn(n: i64) -> Async<i64> {
await sleep(n)
return n * 10
}
main :: fn() -> i32 {
results :: await join_all([fetch(3), fetch(1), fetch(2)])
println("${results}") // [30, 10, 20]
return 0
}A function that uses await is async: it declares -> Async<T> and returns a
T. Calling it gives an Async<T>, which await waits on from another async
function and block_on runs to completion from one that is not; main may
await and keep its i32. The combinators are join_all, try_join_all,
race, join_any, timeout and share.
Single-threaded, timer-driven: the scheduler state (a virtual clock + a
min-heap of wake deadlines) lives in the runtime (native C / VM host), reached
through the std/runtime sched_* intrinsics.
Types
TimeoutError :: struct Error completing a timeout when its deadline elapses before the awaited
value is ready.
Fields
ms: i64 The deadline that elapsed, in milliseconds.
Functions
sleep :: fn(ms: i64) -> Async<null> Suspend the current coroutine for at least ms virtual milliseconds. A
factory (it constructs the awaitable, it does not itself await), so it is an
ordinary function returning an Async, not a coroutine.
Parameters
| ms | i64 | the minimum delay, in milliseconds. |
Returns
Async<null> an awaitable that completes once the delay elapses.Example
await sleep(10) // null, 10 ms laterrun_async_main :: fn(root: Async<i32>) -> i32 Drive an async main's root frame to completion and return its exit code.
Compiler-invoked : when main awaits, the program entry calls this with
main()'s Async<i32> frame. Resumes the root ; whenever it parks, blocks
on the nearest armed timer (sched_park) and re-drives, never spins.
Parameters
| root | Async<i32> | the Async<i32> frame produced by calling the async main. |
Returns
i32 main's completed i32 exit code.Errors
- panics on a park with no timer armed: every task is suspended with nothing left to wake it (a deadlock).
block_on :: fn<T>(root: Async<T>) -> T Drive any awaitable to completion from synchronous code, returning its
result. Resumes root and, whenever it parks, blocks on the nearest armed
timer (sched_park) then re-drives, never spins. Needs no async main: it
works over the global scheduler state, so a plain sync caller (e.g. the CLI
spawning subprocesses) can fan out awaitables and join them here.
Parameters
| root | Async<T> | the awaitable to run to completion. |
Returns
T its completed value.Errors
- panics on a park with nothing armed to wake it (a deadlock).
Example
block_on(sleep(10)) // null: the caller need not be asyncshare :: fn<T>(task: Async<T>) -> Async<T> Share task between any number of awaiters : the first resume drives it, and
once it completes every later one hands back its result, where a finished
coroutine would answer Suspended. Cancelling the shared awaitable leaves the
task to the others. The task an async @singleton hands every call.
Parameters
| task | Async<T> | the awaitable to share. |
Returns
Async<T> an awaitable every awaiter may await.Errors
- panics when the task awaits its own shared awaitable.
Example
once :: share(sleep(10))
await join_all([once, once]) // [null, null], one timer for bothjoin_all :: fn<T>(xs: Async<T>[]) -> Async<T[]> Await a group of awaitables concurrently, yielding their results in input
order. await join_all(xs) completes when every element of xs has completed.
Parameters
| xs | Async<T>[] | the awaitables to join. |
Returns
Async<T[]> an awaitable of the results, index-aligned with xs.Example
await join_all([sleep(20), sleep(10)]) // [null, null], after 20 mstry_join_all :: fn<U, E>(xs: Async<U | E>[]) -> Async<(U | E)[] | Error> Concurrent join of FALLIBLE awaitables (children of type U | E). Runs every
child (like join_all: no cancellation in v1, so failure latency is the slowest
child), then (scanning in input order) completes with the FIRST error, or
the full results if none failed.
The successes come back as (U | E)[], each element typed as the child union:
narrow per element at the use site if you need the raw U.
Parameters
| xs | Async<U | E>[] | the fallible awaitables to join. |
race :: fn<T>(xs: Async<T>[]) -> Async<T> Race a group of awaitables : complete with the FIRST to settle (value or
error), cancelling the losers so they leave no orphaned work. Unlike
join_all, the return type is a single T: the winner's result.
Parameters
| xs | Async<T>[] | the awaitables to race. |
Returns
Async<T> an awaitable of the first to settle.Example
await race([sleep(20), sleep(10)]) // null, after 10 ms: the 20 ms sleep is cancelledjoin_any :: fn<U, E>(xs: Async<U | E>[]) -> Async<U | E> Await a group of FALLIBLE awaitables (children of type U | E), completing
with the FIRST success and cancelling the rest. If every child fails,
completes with the last error. The dual of try_join_all (first success vs first
error).
Parameters
| xs | Async<U | E>[] | the fallible awaitables to race for a success. |
Returns
Async<U | E> the first successful result, or the last error if all fail.timeout :: fn<T>(ms: i64, x: Async<T>) -> Async<T | TimeoutError> Await x, but fail with a TimeoutError if it is not ready within ms
virtual milliseconds. The losing side (the child on timeout, the timer on
success) is cancelled.
Parameters
| ms | i64 | the deadline, in milliseconds. |
| x | Async<T> | the awaitable to bound. |
Returns
Example
await timeout(10, sleep(50)) // a TimeoutError, after 10 ms
await timeout(50, sleep(10)) // null, after 10 ms