Structured Concurrency
Graph gateways become named durable branches. Use Dart if / switch for decisions over recorded values. Use parallel, race, and quorum when more than one await must run together.
dart
final result = await workflow.race(
id: 'providers',
branches: {
'internal': (branch) async {
final draft = await branch.serviceTask(prepare, input, id: 'prepare');
return branch.userTask(
review,
id: 'review',
title: 'Review prepared result',
input: draft.toJson(),
);
},
'external': (branch) => branch.call(providerWorkflow, input, id: 'call'),
},
onCancel: {
'internal': (branch) async {
await branch.serviceTask(releaseDraft, input.id, id: 'release');
},
},
);| Join | Resolves when | Losers |
|---|---|---|
parallel | every named branch completes | none |
race | the first durable completion | cancelled in the same transaction |
quorum | the required number complete | cancelled in the same transaction |
A branch is a workflow scope, not a single task. It may contain service tasks, user tasks, signals, timers, calls, and nested joins.
Loser cancellation prevents pending or claimed commands from reporting a late completion and propagates to attached spawned runs. It cannot undo an external side effect that already happened. Use onCancel for branch-specific cleanup and saga for reverse-order compensation.
JSON documents name branch start steps and branchEnd terminals.