Control Flow Patterns

Common distributed control flow patterns are really simple to implement in workflows and require learning no new syntax. You can just use familiar async/await patterns.


Sequential Execution

The simplest way to orchestrate steps is to execute them one after another, where each step can be dependent on the previous step.

export async function dataPipelineWorkflow(data: any) {
  "use workflow";

  const validated = await validateData(data);
  const processed = await processData(validated);
  const stored = await storeData(processed);

  return stored;
}

Parallel Execution

When needing to execute multiple steps in parallel, you can use Promise.all to run them all at the same time.

export async function fetchUserData(userId: string) {
  "use workflow";

  const [user, orders, preferences] = await Promise.all([ 
    fetchUser(userId), 
    fetchOrders(userId), 
    fetchPreferences(userId) 
  ]); 

  return { user, orders, preferences };
}

This not only applies to steps - since sleep() and webhook are also just promises, we can await those in parallel too. We can also use Promise.race instead of Promise.all to stop executing the promises after the first one completes.


import { sleep, createWebhook } from "workflow";

export async function runExternalTask(userId: string) {
  "use workflow";

  const webhook = createWebhook();
  await executeExternalTask(webhook.url); // Send the webhook somewhere

  // Wait for the external webhook to hit, with a timeout of 1 day,
  // whichever comes first
  await Promise.race([ 
    webhook, 
    sleep('1 day'), 
  ]); 

  console.log("Done")
}

A full example

Here's a simplified example taken from the birthday card generator demoExternal link, to illustrate how more complex orchestration can be modelled in promises.

import { createWebhook, sleep } from "workflow"

async function birthdayWorkflow(
    prompt: string,
    email: string,
    friends: string[],
    birthday: Date
) {
    "use workflow";

    // Generate a birthday card with sequential steps
    const text = await makeCardText(prompt)
    const image = await makeCardImage(text)

    // Create webhooks for each friend who's invited to the birthday party
    const webhooks = friends.map(_ => createWebhook())

    // Send out all the RSVP invites in parallel steps
    await Promise.all(
        friends.map(
            (friend, i) => sendRSVPEmail(friend, webhooks[i])
        )
    )

    // Collect RSVPs as they are made without blocking the workflow
    let rsvps = []
    webhooks.map(
        webhook => webhook
            .then(req => req.json())
            .then(( { rsvp } ) => rsvps.push(rsvp))
    )

    // Wait until the birthday
    await sleep(birthday)

    // Send birthday card with as many rsvps were collected
    await sendBirthdayCard(text, image, rsvps, email)

    return { text, image, status: "Sent" }
}