# c
```

Now, "hello" is printed only once, as expected.

</GuideSection>

</GuideSelector>

## Loop example

<GuideSelector
  options={[
    { key: "typescript", title: "TypeScript" },
    { key: "go", title: "Go" },
    { key: "python", title: "Python" },
  ]}>

<GuideSection show="typescript">
Here's [an example](/blog/import-ecommerce-api-data-in-seconds) of an Inngest function that imports all products from a Shopify store into a local system. This function iterates over all pages combining all products into a single array.

```typescript
export default inngest.createFunction(
  { id: "shopify-product-import"},
  { event: "shopify/import.requested" },
  async ({ event, step }) => {
    []
    let cursor = null
    let hasMore = true

    // Use the event's "data" to pass key info like IDs
    // Note: in this example is deterministic across multiple requests
    // If the returned results must stay in the same order, wrap the db call in step.run()
    await database.getShopifySession(event.data.storeId)

    while (hasMore) {
      await step.run(`fetch-products-${pageNumber}`, async () => {
        return await shopify.rest.Product.all({
          session,
          since_id: cursor,
        })
      })
      // Combine all of the data into a single list
      allProducts.push(...page.products)
      if (page.products.length === 50) {
        cursor = page.products[49].id
      } else {
        hasMore = false
      }
    }

    // Now we have the entire list of products within allProducts!
  }
)
```

In the example above, each iteration of the loop is managed using `step.run()`, ensuring that **all non-deterministic logic (like fetching products from Shopify) is encapsulated within a step**. This approach guarantees that if the request fails, it will be retried automatically, in the correct order. This structure aligns with Inngest's execution model, where each step is a separate HTTP request, ensuring robust and consistent loop behavior.

Note that in the example above `getShopifySession` is deterministic across multiple requests (and it's added to all API calls for authorization). If the returned results must stay in the same order, wrap the database call in `step.run()`.

Read more about this use case in the [blog post](/blog/import-ecommerce-api-data-in-seconds).

</GuideSection>
<GuideSection show="go">
Here's an example of an Inngest function that imports all products from a Shopify store into a local system. This function iterates over all pages combining all products into a single array.

```go
!snippet:path=snippets/go/v0_11/examples/working-with-loops.go
```

In the example above, each iteration of the loop is managed using `step.Run()`, ensuring that **all non-deterministic logic (like fetching products from Shopify) is encapsulated within a step**. This approach guarantees that if the request fails, it will be retried automatically, in the correct order. This structure aligns with Inngest's execution model, where each step is a separate HTTP request, ensuring robust and consistent loop behavior.

Note that in the example above `getShopifySession` is deterministic across multiple requests (and it's added to all API calls for authorization). If the returned results must stay in the same order, wrap the database call in `step.Run()`.

Read more about this use case in the [blog post](/blog/import-ecommerce-api-data-in-seconds).

</GuideSection>
<GuideSection show="python">
Here's an example of an Inngest function that imports all products from a Shopify store into a local system. This function iterates over all pages combining all products into a single array.

```python
@inngest.create_function(
    id="shopify-product-import",
    trigger=inngest.TriggerEvent(event="shopify/import.requested")
)
async def shopify_product_import(ctx: inngest.Context):
    all_products = []
    cursor = None
    has_more = True

    # Use the event's "data" to pass key info like IDs
    # Note: in this example is deterministic across multiple requests
    # If the returned results must stay in the same order, wrap the db call in step.run()
    session = await database.get_shopify_session(ctx.event.data["store_id"])

    while has_more:
        page = await ctx.step.run(f"fetch-products-{cursor}", lambda: shopify.Product.all(
            session=session,
            since_id=cursor
        ))
        # Combine all of the data into a single list
        all_products.extend(page.products)
        if len(page.products) == 50:
            cursor = page.products[49].id
        else:
            has_more = False

    # Now we have the entire list of products within all_products!
```

In the example above, each iteration of the loop is managed using `step.run()`, ensuring that **all non-deterministic logic (like fetching products from Shopify) is encapsulated within a step**. This approach guarantees that if the request fails, it will be retried automatically, in the correct order. This structure aligns with Inngest's execution model, where each step is a separate HTTP request, ensuring robust and consistent loop behavior.

Note that in the example above `get_shopify_session` is deterministic across multiple requests (and it's added to all API calls for authorization). If the returned results must stay in the same order, wrap the database call in `step.run()`.

Read more about this use case in the [blog post](/blog/import-ecommerce-api-data-in-seconds).

</GuideSection>

</GuideSelector>

## Best practices: implementing loops in Inngest

To ensure your loops run correctly within [Inngest's execution model](/docs/learn/how-functions-are-executed):

### 1. Treat each loop iterations as a single step

In a typical programming environment, loops maintain their state across iterations. In Inngest, each step re-executes the function from the beginning to ensure that only the failed steps will be re-tried. To handle this, treat each loop iteration as a separate step. This way, the loop progresses correctly, and each iteration builds on the previous one.

### 2. Place non-deterministic logic inside steps

Place non-deterministic logic (like API calls, database queries, or random number generation) inside `step.run` calls. This ensures that such operations are executed correctly and consistently within each step, preventing repeated execution with each function re-entry.

### 3. Use sleep effectively

When using `step.sleep` inside a loop, ensure it is combined with structuring the loop to handle each iteration as a separate step. This prevents the function from appearing to restart and allows for controlled timing between iterations.

## Next steps

- Docs explanation: [Inngest execution model](/docs/learn/how-functions-are-executed).
- Docs guide: [multi-step functions](/docs/guides/multi-step-functions).
- Blog post: ["How to import 1000s of items from any E-commerce API in seconds with serverless functions"](/blog/import-ecommerce-api-data-in-seconds).