This guide explains the new asynchronous I/O primitives in Fun and how to use them to build non-blocking network and file descriptor workflows. It covers the core concepts, available helpers, common patterns, and runnable examples from the repository.
## What is asyncio in Fun?
In Fun, "asyncio" refers to event-driven, non-blocking I/O built around file descriptor readiness. Instead of blocking on reads/writes, you:
- Put descriptors (sockets, pipes, etc.) into non-blocking mode
- Wait for them to become readable/writable using polling helpers
- Perform small, incremental reads/writes when the OS signals readiness
This lets a single Fun script handle many concurrent connections efficiently without threads, and keeps UIs or other work responsive while I/O is in flight.
There is no special syntax (like async/await) — you compose ordinary control flow with a few focused opcodes and stdlib functions.
However, for more ergonomic, "await-like" workflows without changing the VM, a tiny cooperative scheduler is provided in the stdlib at lib/async/scheduler.fun. It lets you write small step functions that advance per tick and use await_read/await_write wrappers for readability.
- timeout_ms controls how long poll waits. Use small timeouts inside loops to interleave work across multiple sockets or tasks.
- A timeout result (0) is not an error — treat it as an opportunity to perform other duties and try again later.
- Negative results (<0) indicate OS-level errors from poll/select; handle or abort as appropriate.
## Working with multiple connections
To multiplex several sockets:
- Keep per-connection state (outgoing buffer, accumulate incoming, progress markers)
- Round-robin over connections, polling each for read/write readiness with short timeouts
- Advance each state machine a little per iteration
Because Fun keeps the primitives low-level and explicit, you can build simple cooperative schedulers, connection pools, or protocol handlers directly in Fun code.
## Examples in the repository
- examples/io/async_http_client.fun — Minimal HTTP GET over non-blocking TCP using fd_poll_* helpers
The file lib/async/scheduler.fun provides a minimal cooperative scheduler built on the existing primitives. There is no VM-level suspension: each task is a small state machine advanced one step per tick. API summary:
- task_spawn(step_fn, state_map) → task_handle
- Registers a task. step_fn is a function that takes a Map state; mutate state and set state.done = 1 when complete.
- run_once() → 1
- Performs one scheduling tick over all runnable tasks.
- run_until_done() → 1
- Repeats run_once() with a tiny sleep_ms(1) until all tasks finish.
- await_read(fd, timeout_ms) → int
- Wrapper over fd_poll_read; returns 1 if readable, 0 on timeout/EOF, -1 on error.
- await_write(fd, timeout_ms) → int
- Wrapper over fd_poll_write; returns 1 if writable, 0 on timeout, -1 on error.
- yield() → 1
- No-op helper to make intent explicit in step functions.
- async_sleep_mark(state, ms) → 1
- Mark the task to be skipped for roughly ms milliseconds; cleared automatically when it wakes.
A: The helpers map to portable OS facilities exposed by the VM. Details may vary by platform; see documentation/troubleshooting.md and open an issue if you hit differences.