Skip to content

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');
    },
  },
);
JoinResolves whenLosers
parallelevery named branch completesnone
racethe first durable completioncancelled in the same transaction
quorumthe required number completecancelled 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.

See Also