Skip to main content

Spawn & Await

spawn starts a function now on the fiber pool and returns a future. await reads that future's result, suspending the caller until it completes (and re-throwing any error as Async error: …).

Reference

std.async.spawn(fn[, args...]) // run fn(args...) now → future
std.async.await(future) // → result or throws Async error

spawn copies its args by value (deep copy, like assignment). Use globals or ref aliasing if the task must see live shared state (see Overview and Types).

Basic usage

var f = std.async.spawn(fn() { return 42 })
print(std.async.await(f)) // 42

var g = std.async.spawn(fn(a, b) { return a + b }, 10, 20)
print(std.async.await(g)) // 30

await may be called from the main script or from another fiber — it always yields until the target completes.

Why spawn — overlapping work

The point is concurrency: spawned tasks run on the same worker pool and overlap while the caller waits. Compare serial vs concurrent for three 300 ms jobs:

var job = fn(name) { std.async.sleep(300); return name }

// serial — 900 ms (one finishes before the next starts)
var t0 = std.time.now()
job("a"); job("b"); job("c")
print("serial: ", std.time.elapsed(t0), "ms") // e.g. 902 ms

// concurrent — ~300 ms (all three overlap)
var t1 = std.time.now()
var f1 = std.async.spawn(job, "a")
var f2 = std.async.spawn(job, "b")
var f3 = std.async.spawn(job, "c")
var results = [std.async.await(f1), std.async.await(f2), std.async.await(f3)]
print("concurrent:", std.time.elapsed(t1), "ms", results) // e.g. 301 ms ["a","b","c"]

Use this whenever you have independent I/O — fetching several HTTP endpoints, processing several files, or running parallel std.time.timer work.

Full fan-out example with HTTP (all fetches overlapped):

var urls = ["https://a.example/1", "https://b.example/2", "https://c.example/3"]
var futures = []
for (u : urls) {
futures:insert(std.async.spawn(fn(u) {
return std.net.http_client().request({url: u}).body
}, u))
}
var bodies = []
for (f : futures) { bodies:insert(std.async.await(f)) } // parallel

Future lifecycle

  • A future is ready after the fiber returns or throws. Awaiting consumes it: a second await on the same future throws INVALID_OPERAND_TYPE ("Future not found or already awaited").
  • Awaiting discards the future, so don't await a future you still need to pass around. If you need to read a value more than once, copy it out of the result first.
  • There is no cancel for spawned futures — if you need cancellable work use Defer or std.time.timer with cancel().
  • race returns a future for the winner — see Race.

See also

  • Overview — fiber yield points
  • Sleep — how sleep yields while tasks run
  • Patterns — fan-out and aggregation
  • Errors — errors are stored on the future and re-thrown by await