06 โ Async Execution
AsyncNode subclasses run I/O-bound steps (HTTP, DB, file reads) via
Engine.run_async. Sync and async nodes can be mixed in the same graph โ
the engine dispatches each node to the right path automatically.
๐ฏ Goal
Fetch a document from a remote service, then run a synchronous post-processor.
๐ Run it
๐ How dispatch works
Engine.run_async checks each node at execution time:
AsyncNode.resolve()is a no-op (returns()), so a sync engine (run) treats async nodes as if they pruned the branch.AsyncNode.resolve_async()may be a plainasync defreturning an iterable, or an async generator โ both are supported.run_asyncvalidates that yielded objects areState(raisesTypeErrorotherwise), exactly like the sync path.
๐ Async step-wise variant
run_steps_async pairs with async nodes for progress + resume:
๐ก Tips
- Async nodes and sync nodes interleave freely โ no need to split phases.
- For true concurrency across branches, combine async nodes with a library like
asyncio.gatherinside a single wrapper node. - Don't call
run_asyncon an engine whose nodes are all sync โ you pay event loop overhead for nothing;runis fine there. - Cancellation patterns (timeouts, retries) belong inside
resolve_async, not the engine โ keep the engine generic.