Skip to content

Part VI: Concurrency

In this part:


Jac supports Python-style async/await for concurrent I/O operations, plus a unique flow/wait syntax for launching and collecting parallel tasks. Use async when you need non-blocking I/O (like HTTP requests), and flow when you want to run multiple independent operations concurrently.

The async/await syntax works like Python’s — async marks a function as a coroutine, and await suspends execution until the awaited operation completes. Walkers can also be async, enabling non-blocking graph traversal with I/O at each node.

async def fetch_data(url: str) -> dict {
response = await http_get(url);
return await response.json();
}
async def process_multiple(urls: list[str]) -> list[dict] {
results = [];
for url in urls {
data = await fetch_data(url);
results.append(data);
}
return results;
}
async walker DataFetcher {
has url: str;
async can fetch with Root entry {
data = await http_get(self.url);
report data;
}
}

Use async walker for non-blocking I/O during traversal.

async def process_stream(stream: AsyncIterator) -> None {
async for item in stream {
print(item);
}
}

The flow/wait pattern provides explicit concurrency control. flow launches a task and immediately returns a future (without blocking), while wait retrieves the result (blocking if necessary). This is more explicit than async/await — you decide exactly when to start parallel work and when to synchronize.

The flow keyword launches a function call as a background task and returns a future immediately. Use it when you have independent operations that can run in parallel.

def expensive_computation -> int {
return 42;
}
def do_something_else -> int {
return 1;
}
with entry {
future = flow expensive_computation();
# Do other work while computation runs
other_result = do_something_else();
# Wait for result when needed
result = wait future;
}
def fetch_users -> list {
return [];
}
def fetch_orders -> list {
return [];
}
def fetch_inventory -> list {
return [];
}
def process_local_data {
# Process local data here
}
with entry {
# Launch multiple operations in parallel
future1 = flow fetch_users();
future2 = flow fetch_orders();
future3 = flow fetch_inventory();
# Continue with other work
process_local_data();
# Collect all results
users = wait future1;
orders = wait future2;
inventory = wait future3;
}
Featureasync/awaitflow/wait
ModelEvent loop (cooperative)Thread pool (parallel)
Best forI/O-bound, many concurrentCPU-bound, few concurrent
BlockingNon-blockingCan block threads

Related Reference: