Steps

Steps are the atomic units of a workflow. Each step is a generator that yields control to the runtime, which handles caching, execution, and scheduling. Steps must be yielded with yield* (using plain yield produces a runtime error).

step.run

Executes a function and caches its return value. On subsequent workflow invocations, the cached result replays without re-executing the function.

const result = yield* step.run(() => {
  return fetchUser(userId);
});

step.run accepts sync or async functions:

const data = yield* step.run(async () => {
  const res = await fetch("https://api.example.com/data");
  return res.json();
});

step.delay

Pauses the workflow for a given number of milliseconds. The runtime persists the resume timestamp, terminates the current execution, and schedules a wake-up.

yield* step.delay(30_000); // pause for 30 seconds

When the timer fires, the runtime re-invokes the workflow. All steps before the delay replay from cache, and execution continues from the next step.

step.poll

Repeatedly evaluates a predicate at a fixed interval until it returns true or exhausts the retry budget. Internally, step.poll composes step.run with RetryableError.

yield* step.poll(
  { retryInterval: 1000, maxAttempts: 10 },
  async () => {
    const status = await checkDeployment(deployId);
    return status === "ready";
  }
);

If the predicate returns false on every attempt, the step throws after maxAttempts. If the predicate throws a non-retryable error, execution stops immediately.

Cache keys

YieldStar derives a cache key for each step automatically from the call site. This works for straight-line workflows, but breaks inside loops — every iteration shares the same call site.

In loops, provide an explicit cache key as the first argument:

for (let i = 0; i < items.length; i++) {
  yield* step.run(`process-item:${i}`, () => processItem(items[i]));
}

All three step primitives accept an optional key:

yield* step.run("key", fn);
yield* step.delay("key", ms);
yield* step.poll("key", opts, predicate);

If two steps in the same execution resolve to the same cache key, the runtime throws: "Each step in a loop must have a unique cache key."