Task
Task module: Elixir-style lightweight async tasks for one-off concurrent work.
Tasks provide a simple interface for running functions asynchronously without the ceremony of defining a full actor. They build on the task_spawn / task_await interpreter primitives (Phase 1: single-threaded cooperative scheduler — tasks execute eagerly at spawn time).
Basic usage:
let t = Task.async(fn () -> expensive_computation())
let result = Task.await(t) -- Ok(value) or Err(reason)Parallel map:
let results = Task.async_stream([1, 2, 3], fn n -> n * n)
-- results : List(Ok(Int) | Err(String)), in original orderMultiple tasks:
let t1 = Task.async(fn () -> fetch_user(1))
let t2 = Task.async(fn () -> fetch_user(2))
let [r1, r2] = Task.await_many([t1, t2])Note on overloading: March does not support function overloading. Use await_ms/await_many_ms for explicit timeout variants. Use async_stream_n for a concurrency-capped parallel map.
Functions
Wait for all tasks to finish, collecting every result regardless of failure.
Unlike await_many, all_settled does not short-circuit on error. Every
element of the returned list is Ok(v) or Err(reason).
Example:
let tasks = List.map(items, fn x -> Task.async(fn () -> process(x)))
let results = Task.all_settled(tasks)
-- each element is Ok(processed) or Err(reason)Return the first Ok result; if all tasks fail, return Err(List(reasons)).
Waits for the first success and cancels the remaining tasks. If every task
returns Err, collects all reasons and returns Err(reasons_list).
In Phase 1 (interpreter), tasks run eagerly in spawn order; "first Ok" is
the first Ok in list order.
Example:
let tasks = List.map(endpoints, fn ep -> Task.async(fn () -> fetch(ep)))
Task.any(tasks) -- first endpoint that responds successfullySpawn f as a lightweight async task. f must be a zero-argument function: fn () -> result. Returns a Task handle that can be passed to Task.await.
Example:
let t = Task.async(fn () -> 1 + 1)
Task.await(t) -- Ok(2)Run f on each element of list concurrently, returning results in original order. Equivalent to a parallel Enum.map. Each result is Ok(v) or Err(reason).
Example:
Task.async_stream([1, 2, 3], fn n -> n * n)
-- [Ok(1), Ok(4), Ok(9)]Run f on each element concurrently with a cap on parallel tasks. max_concurrency limits how many tasks run at once (advisory in Phase 1). Returns results in original order; each element is Ok(v) or Err(reason).
Example:
Task.async_stream_n(urls, fn url -> http_get(url), 10)Await the result of a task with a default 5-second timeout. Returns Ok(result) or Err(reason).
Example:
let t = Task.async(fn () -> 42)
Task.await(t) -- Ok(42)Await all tasks, returning results in the same order as the input list. Each element is Ok(v) or Err(reason). Uses a default 5-second timeout per task.
Example:
let t1 = Task.async(fn () -> 1)
let t2 = Task.async(fn () -> 2)
Task.await_many([t1, t2]) -- [Ok(1), Ok(2)]Await all tasks with an explicit timeout in milliseconds. Returns results in the same order as the input list. Each element is Ok(v) or Err(reason).
Await the result of a task with an explicit timeout in milliseconds. Returns Ok(result) or Err(reason). timeout_ms is accepted for API compatibility; enforcement requires the async runtime (Phase 2+).
Example:
let t = Task.async(fn () -> slow_query())
Task.await_ms(t, 10000) -- wait up to 10 secondsUnwrap the result of a task, raising on error. Returns the result value directly (not wrapped in Ok). Panics if the task failed.
Example:
let t = Task.async(fn () -> 42)
Task.await!(t) -- 42Return the result of the first task that completes; cancel the rest.
In Phase 1 (interpreter), all tasks have already run eagerly at spawn
time, so race returns the first task's result and marks the remainder
cancelled. In Phase 2+ (multi-core runtime), the first task to actually
finish wins and the others are cancelled via their shared cancel token.
Returns Err("empty") if the list is empty.
Example:
let t1 = Task.async(fn () -> slow_query())
let t2 = Task.async(fn () -> cached_query())
Task.race([t1, t2]) -- whichever finishes firstRun f inside a structured concurrency scope.
Any tasks spawned with Task.async inside f that are still pending when f
returns (or raises) are cancelled automatically.
In Phase 1 (interpreter), all tasks complete at spawn time, so this is
equivalent to calling f directly. In Phase 2+ (multi-core runtime), a
shared cancel token is injected and all in-flight tasks are cancelled on
scope exit.
Returns Ok(result) if f completes normally, or Err(reason) if it raises.
Example:
Task.scope(fn () ->
let t1 = Task.async(fn () -> fetch_a())
let t2 = Task.async(fn () -> fetch_b())
[Task.await_unwrap(t1), Task.await_unwrap(t2)]
)