Workflow steps and waits

Sleep and wait without holding compute

Pause for time or an external event while preserving workflow state and avoiding a long-running request.

8 minute lesson

~~~

A Workflow can pause for a day, a month, or until a human clicks a button, and it costs nothing while paused. Workflows can sleep until a duration or time and can wait for external events. The workflow does not need a Worker request or JavaScript timer to remain alive — the engine persists its position and resumes it when the wait ends.

Time-based waits are one line:

await step.sleep('wait for trial to end', '14 days')
await step.sleepUntil('send on launch morning', new Date('2026-09-01T08:00:00Z'))

Compare that with what it replaces: a cron job scanning a table of pending trials, state flags, and edge cases around restarts. Here the “what happens next” logic reads top to bottom in one function.

Event-based waits pause until something outside delivers a matching event:

const approval = await step.waitForEvent('wait for manager approval', {
  type: 'approval',
  timeout: '3 days',
})

The other side sends the event through the instance handle:

const instance = await env.REVIEW_WORKFLOW.get(reviewId)
await instance.sendEvent({ type: 'approval', payload: { approvedBy: userId } })

The type must match the waitForEvent call, and the sender needs the instance ID. That’s why you give instances stable IDs — create the workflow with id: reviewId and the approval endpoint can always address the right instance. An authenticated webhook or application action can then deliver the correct event without any lookup table.

Events are input — treat them as untrusted

The event payload arrives from your endpoint, and your endpoint is reachable. Validate event type, tenant, state, and replay behavior before acting: is this approver allowed to approve this tenant’s review, is the workflow actually in a waiting state, and has this approval already been consumed? A duplicate approval event should be recognized and ignored, not processed twice.

Define timeouts and the path for an event that never arrives. waitForEvent throws when its timeout expires — the default is 24 hours — so catch it and decide: escalate, auto-reject, or remind. A workflow with no timeout plan doesn’t fail, it just waits, invisibly, forever.

Build a review workflow that waits for approval, times out after a practice interval, and rejects duplicate approval events. Use a short timeout like two minutes so you can watch all three paths — approved, timed out, duplicate — actually run.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →