Skip to content

Parallel Execution

Parallel execution lets you run independent steps concurrently inside a flow.

This is useful when multiple operations do not depend on each other and you want to reduce overall execution time without splitting the workflow into separate flows.

When to use parallel steps

Parallel execution is a good fit for work like:

  • sending independent notifications
  • writing an audit log while also publishing an event
  • fetching unrelated data from multiple sources

It is not a good fit when one step depends on data produced by another step in the same group.

Defining a parallel block

ts
flow
  .parallel("fetch-data", [
    {
      name: "fetch-user",
      fn: async (ctx) => { /* ... */ }
    },
    {
      name: "fetch-posts",
      fn: async (ctx) => { /* ... */ }
    }
  ])
  .step("process-all", (ctx) => {
    // Runs after the whole parallel block finishes
  });

The next step after the block only starts once every step in the group has finished.

How it works

Inside a parallel block:

  1. all step functions are started
  2. each step still gets its own retries, timeout handling, and result object
  3. the flow waits until the entire group settles
  4. the group outcome is evaluated according to failFast

Default failure behavior

By default, a parallel block does not fail the flow just because one step failed.

Instead, the block only fails the flow when all steps in that parallel group fail.

This means a mixed result like:

  • fetch-user: completed
  • fetch-posts: failed

can still allow the flow to continue if failFast is not enabled.

This behavior is important and should be chosen intentionally.

failFast: true

If you want any failed step to fail the whole group, use:

ts
flow.parallel("fetch-data", [
  { name: "fetch-user", fn: async () => {} },
  { name: "fetch-posts", fn: async () => {} }
], {
  failFast: true
});

With failFast: true, a single failed step is enough to make the group fail.

How to choose the right mode

Use the default mode when:

  • partial success is acceptable
  • the downstream flow can continue with missing or degraded data
  • the group contains optional work

Use failFast: true when:

  • all operations in the group are required
  • partial success would leave the workflow in an invalid business state
  • you want strict all-or-nothing behavior for that group

Parallel steps and compensation

Parallel steps can also define compensation.

If the group fails and some parallel steps already completed successfully, those completed steps may be compensated as part of the failure path.

That means the same rollback discipline used for sequential steps should also be applied inside parallel groups.

Best Practices

  • Use parallel blocks only for independent work.
  • Be explicit about whether partial success is acceptable.
  • Add compensation to side-effecting parallel steps.
  • Keep group size reasonable to avoid creating bursts against downstream systems.

Released under the MIT License.