Conditions & Branching
Workflows often need to branch: run different paths depending on data, skip steps when conditions aren’t met, or wait for a combination of signals before proceeding. Both durable tasks and DAGs support conditional logic, but the approach differs.
Procedural Branching
Durable tasks use standard language control flow (if/else, match, loops) to branch at runtime. Because the task is a single long-running function, you can make decisions based on any data available during execution: inputs, intermediate results, API responses, or child task outputs.
@workflow.durable_task()
async def process(input: ProcessInput, ctx: DurableContext):
result = await ctx.run_child(analyze_task, input)
if result["score"] > 0.8:
await ctx.run_child(fast_path_task, result)
else:
await ctx.run_child(slow_path_task, result)
await ctx.run_child(review_task, result)This is one of the key advantages of durable tasks: branching logic is expressed directly in code, making it easy to handle complex, dynamic flows. Each branch can spawn different children, sleep for different durations, or wait for different events.
Branching logic must be deterministic between checkpoints. If the task is evicted and replayed, the same branches must execute in the same order. Base decisions on checkpoint outputs (child results, event payloads) rather than wall-clock time or external state that may change between replays. See Best Practices for details.
Or Groups
Durable tasks can combine multiple wait conditions using or groups. An or group evaluates to True if at least one of its conditions is satisfied, letting you express “proceed on timeout or event, whichever comes first.”
or_() wraps a SleepCondition and a UserEventCondition into a single or group. The task will resume as soon as either the sleep expires or the event arrives.