CLS has first-class support for asynchronous programming through a coroutine model built on top of theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/elfrask/cls/llms.txt
Use this file to discover all available pages before exploring further.
Promise and Pollable abstractions in the runtime. An async function is a function whose body is wrapped in a coroutine: calling it does not execute the body immediately — it returns a Promise. The body only runs when that promise is polled, which happens when you await it. The async module provides utility functions for composing and scheduling multiple promises.
Import
Core Language Keywords
async function
Declaring a function with the async modifier causes the interpreter to wrap its body in a CoroutineTask. Calling the function returns a Value::Promise without running any of the body yet.
await
The await expression polls a Promise to completion. It must be used inside an async function. The expression evaluates to the resolved value of the promise.
Using
await outside an async function is a runtime error. The promise will be polled but the enclosing execution context does not have a scheduler to suspend on Pending, so Pending simply returns Void.async Module Functions
async.delay(ms: int) -> Promise
Returns a Promise that resolves to void after the given number of milliseconds. Internally it spawns a background thread that sleeps for ms ms, and the promise becomes ready when that thread joins.
async.all(promises: Array) -> Promise
Takes an array of Promise values and returns a new Promise that resolves to an Array containing all of the results, in the same order as the input. The composed promise only resolves once every input promise has resolved. If any promise rejects, async.all rejects immediately.
async.race(promises: Array) -> Promise
Takes an array of Promise values and returns a new Promise that resolves with the result of whichever input promise resolves first. Remaining promises are not awaited.
Complete Examples
Simple async/await
Chaining async calls
Waiting for multiple operations with async.all
First-to-resolve with async.race
How Coroutines Work Internally
When the interpreter encounters anasync function call, it wraps the function body and its bound arguments in a CoroutineTask (a Pollable). That task is boxed inside a Promise. No code in the body runs at this point.
When await is evaluated:
- The interpreter calls
Promise::poll(self)on the wrappedPollable. CoroutineTask::pollruns the async body synchronously to its next yield point.- If the body completes,
PollState::Ready(value)is returned andawaitresolves to that value. - If the body is still waiting on a nested delay or I/O,
PollState::Pendingis returned andawaityieldsVoidin the current scheduler tick.
async.delay uses a real OS thread sleeping in the background. async.all polls promises sequentially; async.race polls them in order and returns the first Ready result.
The CLS scheduler is currently cooperative and single-threaded. True parallel execution of
async.all branches does not happen — each promise is polled in sequence within a single tick. For I/O-bound concurrency, use async.race or structure your code so that the delay work happens in background threads (as async.delay already does).