Coroutines
Run waiting work side by side and await the results.
On this page
A coroutine is a function call that runs alongside others on the same thread. While one waits for a timer, a socket or a file, the others keep going.
Start and await#
co starts a call as a coroutine and await waits for its result:
use valk.time
fn slow_double(value: int) int {
time.sleep_ms(20)
return value * 2
}
fn main() {
let first = co slow_double(10)
let second = co slow_double(20)
println((await first) + (await second)) // 60, after about 20 ms
}
Both calls wait at the same time, so the program takes about as long as one of them. Functions that wait, like time.sleep_ms, network calls and file reads, let other coroutines run in the meantime.
The coroutine type#
co returns a value of type co(T), which can be stored, passed around and awaited later. Several results and an error type are written as in a function signature:
fn min_max(a: int, b: int) (int, int) {
return a < b ? (a, b) : (b, a)
}
fn start() co(int, int) {
return co min_max(7, 3)
}
fn main() {
let task = start()
let low, high = await task
println("%low %high") // 3 7
}
Errors#
An error thrown in a coroutine is handled at the await:
fn parse_count(text: String) int !SyntaxError {
return text.to(int) !>
}
fn main() {
let good = co parse_count("42")
let bad = co parse_count("many")
println(await good !? 0) // 42
println(await bad !? 0) // 0
}
- A coroutine that is never awaited drops its error silently.
- Errors thrown by a coroutine cannot carry payload fields; handle those inside the coroutine.
Run a closure#
co also starts a closure, which can capture local values:
use valk.time
fn main() {
let name = "Ada"
let task = co fn() String {
time.sleep_ms(5)
return "hello " + name
}()
println(await task) // hello Ada
}
Many coroutines#
Keep the coroutines in an array and await them in turn:
use valk.time
fn work(id: int) int {
time.sleep_ms(10)
return id
}
fn main() {
let tasks = Array[co(int)]{}
each 0..10 as id : tasks.append(co work(id))
let total = 0
each tasks as task : total += await task
println(total) // 45
}
To wait for whichever finishes first, send results over a channel.
Things to know#
- Coroutines share one thread. CPU-heavy work does not get faster; use threads for that.
coro.await_last()fromvalk.corolets the other coroutines run before continuing.core.MutexandLock[T]waits only pause the current coroutine. See Sharing data between threads.- Each coroutine has a 1 MB stack; very deep recursion ends with a
Stack overflowpanic. Keep large buffers in anArrayinstead of a fixed array.
