Home › Language Reference › Async / Await
Async / Await
Aether supports async programming with async fn, await, and a configurable I/O thread pool for concurrent I/O operations.
async fn
Declaring a function async makes it lazy — calling it returns a Promise immediately without executing the body.
async fn fetch_user(id) {
let data = await http_get("/users/" + id)
return data
}
let p = fetch_user(42) // returns a Promise, body not yet run
let user = await fetch_user(42) // executes body and returns result
Anonymous async functions work the same way:
let double = async fn(x) { return x * 2 }
let result = await double(10) // 20
await
await resolves a Promise by executing its deferred body and returning the result. The result is cached — awaiting the same Promise twice only runs the body once.
await on a non-Promise value is a no-op and returns the value unchanged:
let x = await 42 // x = 42
.then(callback)
.then(callback) attaches a callback to any value. The callback receives the resolved value as its first argument.
| Value type | When callback fires |
|---|---|
I/O Promise (set_workers active) |
After the I/O completes; enqueued in the event loop |
| CPU/async Promise | Immediately, inline |
| Non-Promise (any value) | Immediately, with the value passed through |
This mirrors on_ready() so the same code works whether or not set_workers is active.
fn main() {
set_workers(2)
let p = sleep(0.1)
p.then(fn(v) {
println("sleep done")
})
// event_loop() drains pending callbacks; or they auto-drain when main() returns
}
Chaining
Callbacks can register further .then() calls to sequence work:
fn main() {
set_workers(2)
let p = http_get("https://api.example.com/users")
p.then(fn(body) {
let users = json_parse(body)
let p2 = http_get("https://api.example.com/posts")
p2.then(fn(body2) {
println("users:", users["count"])
println("posts:", json_parse(body2)["count"])
})
})
}
Auto-drain
When main() returns with pending .then() callbacks still queued, the runtime drains them automatically — no explicit event_loop() call required for simple cases.
Promise combinators
Promise.all(array)
Runs all I/O Promises in the array concurrently and returns an array of results in the same order. Fails fast if any Promise throws.
set_workers(4)
let p1 = http_get("https://api.example.com/users")
let p2 = http_get("https://api.example.com/posts")
let results = await Promise.all([p1, p2])
println(results[0]) // users response
println(results[1]) // posts response
Promise.race(array)
Returns the result of the first Promise to complete. Remaining Promises are discarded.
set_workers(4)
let p1 = http_get("https://primary.example.com/data")
let p2 = http_get("https://backup.example.com/data")
let fastest = await Promise.race([p1, p2])
println(fastest)
Promise.allSettled(array)
Runs all Promises and returns an array of result dicts — one per input — regardless of success or failure. Each dict has "status" ("fulfilled" or "rejected") and either "value" or "reason".
set_workers(4)
let promises = [
http_get("https://api.example.com/a"),
http_get("https://api.example.com/b"),
http_get("https://will-fail.example.com/c")
]
let results = await Promise.allSettled(promises)
for r in results {
if (r["status"] == "fulfilled") {
println("OK:", r["value"])
} else {
println("Failed:", r["reason"])
}
}
I/O thread pool
By default, I/O functions like http_get, http_post, sleep, read_file, and write_file run synchronously and block the main thread. To run them concurrently, enable the I/O thread pool:
set_workers(4) // or set AETHER_IO_WORKERS=4 env var
Once workers are active, calling any I/O function submits the task to the pool and immediately returns a Promise. Use await, Promise.all, or .then() to collect results.
Default worker count: max(cpu_count - 1, 4) when AETHER_IO_WORKERS is set.
Examples
Sequential async
async fn double(x) { return x * 2 }
async fn quadruple(x) {
let a = await double(x)
return await double(a)
}
fn main() {
println(await quadruple(5)) // 20
}
Async with error handling
async fn safe_fetch(url) {
try {
return await http_get(url)
} catch(e) {
return null
}
}
fn main() {
let data = await safe_fetch("https://api.example.com/data")
if (data != null) {
println(data)
}
}
Concurrent file reads
fn main() {
set_workers(4)
let files = ["a.txt", "b.txt", "c.txt"]
let promises = []
for f in files {
promises.push(read_file(f))
}
let contents = await Promise.all(promises)
for c in contents {
println(c)
}
}
Behavior reference
| Situation | What happens |
|---|---|
Call async fn |
Returns a pending Promise; body not executed |
await a pending Promise |
Executes the body, caches the result |
await a resolved Promise |
Returns the cached result immediately |
await a non-Promise |
Returns the value unchanged |
Promise.all |
All I/O runs concurrently; waits for all |
Promise.race |
Returns the first to complete |
Promise.allSettled |
Waits for all; never throws |
.then(callback) |
Callback fires inline (CPU promise / value) or via event loop (I/O promise) |
Related
- HTTP —
http_get/http_postreturn Promises when workers are active - Time —
sleepreturns a Promise when workers are active - JSON — parse HTTP response bodies as structured data
- Configuration —
set_workers,AETHER_IO_WORKERS