Language
Async
Calling an async fun starts it in the background and immediately returns a Future<T>. await waits for the
result.
import "std/time"
async fun download(name: string, ms: int): string {
sleep(ms)
return name + " done"
}
fun main() {
var a = download("first", 200)
var b = download("second", 200)
print(await a)
print(await b)
}Both downloads run at the same time, so the program takes about 200ms instead of 400ms.
Futures
- An
async funreturningTgives aFuture<T>when called; one without a result gives aFuture<void>, which can still be awaited to wait for it to finish. - Futures are values: store them in variables, arrays (
[Future<int>]) or fields, pass them around, and await them later. Printing one shows<future>. await fwaits until the task is done and gives its result. Awaiting the same future again gives the same result at once.awaitworks in any function, not only inasyncones: it simply blocks the caller until the result is there.- An
async funcan be a top level function or a struct method (async fun save(): boolinsidedef struct). Async lambdas are not supported; wrap the work in a namedasync fun.
Starting many tasks and collecting their results:
async fun square(n: int): int {
return n * n
}
[Future<int>] pending = []
for i in 0..5 {
pending.push(square(i))
}
var total = 0
for f in pending {
total += await f
}
print(total)Running and ending
- Every task runs on its own operating system thread, so tasks use several processor cores at once.
- A task that is never awaited still runs to the end: the program waits for all running tasks before it exits.
- A runtime error in a task stops the whole program with the error and exit code
1, as in the main program.
import "std/time"
async fun tick(name: string) {
sleep(50)
print(name)
}
tick("a")
tick("b")
print("main ends")prints main ends first, then a and b once the tasks wake up, and only then exits.
Sharing data
Tasks share global variables and the objects passed to them. Memory management is safe across threads: reference
counting switches to atomic operations while tasks run, so sharing objects never frees them too early. Changing the
same array, map or object from several tasks at the same time is not synchronized, though; give each task its own data
and combine the results after await:
async fun countWords(text: string): int {
var n = 0
for w in split(text, " ") {
if (len(w) > 0) {
n += 1
}
}
return n
}
var parts = ["one two three", "four five", "six"]
[Future<int>] counts = []
for p in parts {
counts.push(countWords(p))
}
var sum = 0
for c in counts {
sum += await c
}
print(sum)On each backend
| bvm and native | JavaScript | |
|---|---|---|
a call to an async fun |
starts a thread and returns at once | runs the function to the end, then returns |
await |
waits for the thread | gives the result that is already there |
sleep in a task |
sleeps only that task | blocks the whole program |
Results are the same on every backend; only the timing differs. Programs built with --no-std cannot use async,
since threads are part of the standard runtime.
See running on bvm and memory management for how tasks work inside.