Timer Event Nodes
Timer event nodes pause workflow execution until a specified time is reached. They support both fixed durations and absolute date/time targets.
Definition
Timer events are defined using TimerEventNodeConfiguration:
final node = LeafWorkflowNode(
id: 'waitForDeadline',
type: NodeType.timerEvent,
name: 'Wait for Deadline',
config: TimerEventNodeConfiguration(
timerType: TimerType.duration,
duration: '24h',
cancellable: true,
storeAs: 'timerResult',
),
);Or in JSON:
{
"id": "waitForDeadline",
"type": "timerEvent",
"name": "Wait for Deadline",
"config": {
"schemaType": "config.timerEvent",
"timerType": "duration",
"duration": "24h",
"cancellable": true,
"storeAs": "timerResult"
}
}Timer Properties
| Property | Type | Description |
|---|---|---|
timerType | TimerType | duration or dateTime |
duration | String? | Duration string (e.g., "5m", "2h30m", "1d") |
dateTime | String? | ISO 8601 date/time (e.g., "2024-12-25T09:00:00Z") |
dateTimeExpression | String? | Expression resolving to a date/time from workflow data |
storeAs | String? | Key for storing timer result in output |
cancellable | bool | Whether the timer can be cancelled externally (default: true) |
Timer Modes
Duration Mode
Wait for a fixed duration from the current time:
TimerEventNodeConfiguration(
timerType: TimerType.duration,
duration: '2h30m', // 2 hours and 30 minutes
storeAs: 'timerResult',
)Supported duration formats:
| Format | Example | Description |
|---|---|---|
| Simple | "5m", "2h", "1d" | Single unit |
| Compound | "1d2h30m", "2h30m15s" | Multiple units combined |
| Milliseconds | "500ms" | Sub-second precision |
Units: d (days), h (hours), m (minutes), s (seconds), ms (milliseconds)
DateTime Mode
Wait until a specific date/time:
// Fixed date/time
TimerEventNodeConfiguration(
timerType: TimerType.dateTime,
dateTime: '2024-12-25T09:00:00Z',
storeAs: 'timerResult',
)
// Dynamic from workflow data
TimerEventNodeConfiguration(
timerType: TimerType.dateTime,
dateTimeExpression: r'$.input.dueDate',
storeAs: 'timerResult',
)The dateTimeExpression resolves against workflow variables and input data at runtime. This is useful when the target time comes from previous task output or workflow input.
How It Works
Step Details:
- Token arrives at timer node
TimerEventNodeProcessorcalculates the fire time- If the fire time is already in the past, the workflow continues immediately with
firedImmediately: true - Otherwise, returns
WaitForTimerResultwithtimerId,firesAt, andcancellableflag - Engine persists the timer and updates status to
waitingForSignal - Timer service checks for fired timers and sends the appropriate signal
- On signal received, executor stores result and workflow continues
Timer Output
When a timer fires, the output stored at storeAs contains:
// Normal fire
{
'timerId': 'uuid-value',
'firesAt': '2024-12-25T09:00:00.000Z',
'firedImmediately': false, // or true if already expired
'cancelled': false,
}
// Cancelled
{
'cancelled': true,
'cancelledAt': '2024-12-20T15:30:00.000Z',
'reason': 'Cancelled by signal',
}Cancellation
Timers with cancellable: true can be cancelled by an external signal. The cancel signal name follows the pattern timer_{nodeId}_cancel:
// Cancel a timer
await engine.sendSignal(
workflowInstanceId: instanceId,
node: 'waitForDeadline',
payload: {
'reason': 'User requested cancellation',
},
);Use Cases
SLA Deadline
final slaTimer = LeafWorkflowNode(
id: 'slaDeadline',
type: NodeType.timerEvent,
name: 'SLA Deadline',
config: TimerEventNodeConfiguration(
timerType: TimerType.duration,
duration: '4h',
cancellable: true,
storeAs: 'slaResult',
),
);Scheduled Processing
final scheduledNode = LeafWorkflowNode(
id: 'waitForBusinessHours',
type: NodeType.timerEvent,
name: 'Wait for Business Hours',
config: TimerEventNodeConfiguration(
timerType: TimerType.dateTime,
dateTimeExpression: r'$.input.scheduledProcessingTime',
storeAs: 'scheduleResult',
),
);Cooldown Period
final cooldown = LeafWorkflowNode(
id: 'cooldownPeriod',
type: NodeType.timerEvent,
name: 'Cooldown Period',
config: TimerEventNodeConfiguration(
timerType: TimerType.duration,
duration: '30m',
cancellable: false,
storeAs: 'cooldownResult',
),
);Reminder with Cancellation
Combine a timer with cancellation for reminder patterns. If the user completes the task before the timer fires, the timer is cancelled:
// Timer that fires after 24 hours
final reminderTimer = LeafWorkflowNode(
id: 'reminderTimer',
type: NodeType.timerEvent,
name: 'Reminder Timer',
config: TimerEventNodeConfiguration(
timerType: TimerType.duration,
duration: '24h',
cancellable: true,
storeAs: 'reminderResult',
),
);Timer vs Signal Wait
| Aspect | Timer Event | Signal Wait |
|---|---|---|
| Trigger | Time-based (automatic) | External system (manual) |
| Configuration | Duration or date/time | Signal name |
| Cancellation | Optional (cancellable flag) | N/A |
| Immediate fire | Yes, if time already passed | No |
| Best for | Delays, deadlines, SLAs | Webhooks, human actions |
Validation
The executor validates timer configuration at build time:
- Duration mode: Duration string must be non-empty and parseable
- DateTime mode: Either
dateTimeordateTimeExpressionmust be provided - DateTime format: Must be valid ISO 8601
- Expression format: Must be a valid path expression (e.g.,
$.input.dueDate)
Best Practices
- Use duration for relative waits - SLAs, cooldowns, retry delays
- Use dateTime for absolute targets - Scheduled processing, business hours
- Use expressions for dynamic times - When the target comes from workflow data
- Enable cancellation - Unless the wait must not be interrupted
- Always use
storeAs- Namespace timer results for downstream access - Handle immediate fires - Check
firedImmediatelyif timing matters
Next Steps
- Signal Wait - External signal-based waits
- Subflow - Subprocess invocation
- Gateways - Routing after timer events
- Error Handling - Timeout patterns