# ✅ Good
user_birthday = await ctx.step.run("get-user-birthday", async () => {
  user = await get_user();
  return user.birthday; # Date
});

await ctx.step.sleep_until("wait-for-user-birthday", user_birthday);
```

### Unexpected loop behavior

When using loops within functions, it is recommended to treat each iteration as it's own step or steps.

When [functions are run](/docs/learn/how-functions-are-executed), the function handler is re-executed from the start for each new step and previously completed steps are memoized. This means that iterations of loops will be run every re-execution, but code encapsulated within `step.run()` will not re-run.

If code within a loop is not encapsulated within a step, it will re-run multiple times, which can lead to confusing behavior, debugging, or [logging](/docs/guides/logging). This is why it is recommended to encapsulate non-deterministic code within a `step.run()` when working with loops.

Learn more about [working with loops in Inngest](/docs/guides/working-with-loops).

</GuideSection>

</GuideSelector>

## Further reading

More information on multi-step functions:
- Docs guide: [working with loops in Inngest](/docs/guides/working-with-loops).
- Blog post: ["Building an Event Driven Video Processing Workflow with Next.js, tRPC, and Inngest
"](/blog/nextjs-trpc-inngest)
- Blog post: ["Running chained LLMs with TypeScript in production"](/blog/running-chained-llms-typescript-in-production)
- Blog post: [building Truckload](/blog/mux-migrating-video-collections), a tool for heavy video migration between hosting platforms, from Mux.
- Blog post: building _banger.show_'s [video rendering pipeline](/blog/banger-video-rendering-pipeline).
- [Email sequence examples](/docs/examples/email-sequence) implemented with Inngest.
- [Customer story: Soundcloud](/customers/soundcloud): building scalable video pipelines with Inngest to streamline dynamic video generation.