---
title: "Parallel steps"
description: "Run steps at the same time, race them, and fan out, with the same outcome on every replay."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.ezghcloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Parallel steps

Steps that a workflow starts together run in parallel. EZGH runs each one as its own invocation,
retried on its own, and invokes the workflow again as they finish.

### TypeScript

`Promise.all`, `Promise.race` and `Promise.any` over steps (including sleeps, callbacks and child
workflows) hand every step to EZGH at once:

```ts
const [crm, billing] = await Promise.all([
  step.run("crm", () => crm.upsert(user)),
  step.run("billing", () => billing.createCustomer(user)),
])

const sizes = await Promise.all(urls.map((url, i) => step.run(`size-${i}`, () => fetchSize(url))))

const first = await Promise.race([
  step.run("work", doWork),
  step.sleep("limit", "10m").then(() => "timed out"),
])
```
### Go

Go has no implicit concurrency: run branches with `Parallel`, `All` or `Race`. Each branch gets
its own `Context` and must call steps with it.

```go
var crm, billing string
err := workflows.Parallel(ctx,
	func(c workflows.Context) (err error) { crm, err = workflows.Run(c, "crm", upsertCRM); return },
	func(c workflows.Context) (err error) { billing, err = workflows.Run(c, "billing", createCustomer); return },
)
```

- `Parallel` returns once every branch has returned: nil, the one error, or the errors joined in
branch order.
- `All` returns at the first error (the other branches keep running), or nil once every branch has
returned, like `Promise.all`.
- `Race` returns the index and error of the first branch to return. The other branches keep
running.
### Python

In an `async def` workflow, `asyncio.gather`, `asyncio.wait` and `asyncio.as_completed` over
steps run them in parallel. A plain `def` workflow uses `ctx.step.parallel`:

```python
crm, billing = await asyncio.gather(
    ctx.step.run("crm", sync_crm),
    ctx.step.run("billing", bill),
)

# In a plain def workflow
a, b = ctx.step.parallel(("part-a", lambda: part(rows, 0)), ("part-b", lambda: part(rows, 1)))
```

## Rules

- **Steps pending at the same time need distinct names** (or explicit IDs), such as `size-${i}`.
- **A run has at most 32 step invocations at once.** Further steps wait their turn, in order.
- **At most 1,000 steps can start together;** more fails the run. Start them in batches, or fan
  out through [child workflows](/workflows/child-workflows).
- **A run has at most 1,000 steps** in all, each child workflow counting as one.
- **Races are deterministic.** A replay resolves recorded steps one at a time in the order they
  finished, so a race takes the same branch every time.
- Each parallel step uses its own [retry policy](/workflows/retries), and a failed step doesn't
  stop its siblings.

Source: https://docs.ezghcloud.com/workflows/parallel-steps/index.mdx
