---
title: "Don’t Overload the API: Sequential & Batched Promises"
description: Learn to juggle JavaScript promises through various throttling methods to avoid overloading the API.
image: https://www.bitovi.com/hubfs/Don%E2%80%99t%20Overload%20the%20API-%20Sequential%20%26%20Batched%20Promises-1.png
---

- ![AI implementation](https://www.bitovi.com/hubfs/AIConsultingIcon.svg)
  
  [AI implementation](https://www.bitovi.com/services/ai-consulting)
- ![Systems engineering](https://www.bitovi.com/hubfs/icon%20-%20backend.svg)
  
  [Systems engineering](https://www.bitovi.com/services/systems-engineering-consulting)
- ![Project Management](https://www.bitovi.com/hubfs/icon%20-%20PM.svg)
  
  [Project Management](https://www.bitovi.com/services/agile-project-management-consulting)
- ![Product Design](https://www.bitovi.com/hubfs/icon%20-%20design.svg)
  
  [Product Design](https://www.bitovi.com/services/product-design-consulting)
- ![Frontend development](https://www.bitovi.com/hubfs/icon%20-%20frontend.svg)
  
  [Frontend development](https://www.bitovi.com/services/frontend-development-consulting)
- [View more
  
  →
  
  ](https://www.bitovi.com/digital-consulting-services)

We're Experts in...

- [JavaScript](https://www.bitovi.com/services/frontend/javascript-consulting)
- [AI training](https://www.bitovi.com/ai-training-for-software-engineers)
- [Angular](https://www.bitovi.com/services/frontend/angular-consulting)
- [Design systems](https://www.bitovi.com/services/axure-figma-migration)
- [React](https://www.bitovi.com/services/frontend/react-consulting)
- [Temporal](https://www.bitovi.com/services/backend/temporal-consulting)
- [React Native](https://www.bitovi.com/services/frontend/react-consulting/react-native)
- [Node.js](https://www.bitovi.com/services/backend/nodejs-consulting)

Showcase

![Yum! Brands](https://www.bitovi.com/hubfs/yum-showcase-link-1.png)

[View case study](https://www.bitovi.com/en/bitovi-yum-case-study)

More Projects

- [![Levi's](https://www.bitovi.com/hubfs/levis.svg)](https://www.bitovi.com/web-application-consulting-work/levis-ecommerce-responsive-redesign)
- [![Christie's International Real Estate](https://www.bitovi.com/hubfs/christies.svg)](https://design.bitovi.com/christies)
- [![BAFS](https://www.bitovi.com/hubfs/bafs.svg)](https://www.bitovi.com/ux-design-consulting/ux-case-studies/bafs-ppp)
- [View more
  
  →
  
  ](https://www.bitovi.com/our-software-consulting-work)

Open Source Tools

We build powerful tools and open source them to support the community.

[See what we've built →](https://www.bitovi.com/open-source)

- [![Blog](https://www.bitovi.com/hubfs/icon%20-%20blog.svg)
  
  BlogWe post about delivering products and solving problems.
  
  ](https://www.bitovi.com/blog)
- [![Partnerships](https://www.bitovi.com/hubfs/Handshake-1.svg)
  
  PartnershipsLearn about Bitovi's technology partners
  
  ](https://www.bitovi.com/partnerships)
- [![Academy](https://www.bitovi.com/hubfs/icon%20-%20academy%20(4).svg)
  
  AcademyFree courses to build delivery skills
  
  ](https://www.bitovi.com/academy)
- [![Open source tools](https://www.bitovi.com/hubfs/icon%20-%20open%20source.svg)
  
  Open source toolsUse or contribute to our community
  
  ](https://www.bitovi.com/open-source)

Let's Connect

- [![Discord](https://www.bitovi.com/hubfs/DiscordLogo.svg)
  
  Discord
  
  ](https://discord.gg/J7ejFsZnJ4)
- [![LinkedIn](https://www.bitovi.com/hubfs/LinkedinLogo.svg)
  
  LinkedIn
  
  ](https://www.linkedin.com/company/bitovi/)
- [![GitHub](https://www.bitovi.com/hubfs/GithubLogo.svg)
  
  GitHub
  
  ](https://github.com/bitovi/)

![Eggbot](https://www.bitovi.com/hubfs/build_assets/bitovi-limbo-cms-react/338/js_client_assets/assets/eggbot-LTGhdSGL.png)

Name *

Work Email *

Phone

What's your project?

Send

### Contact Us

(312) 620-0386contact@bitovi.com

[ React ](https://www.bitovi.com/blog/topic/react) |  May 5, 2023

# Don’t Overload the API: Sequential & Batched Promises

 Learn to juggle JavaScript promises through various throttling methods to avoid overloading the API.

![Ryan Spencer](https://www.bitovi.com/hubfs/Ryan%20Spencer.jpeg)

 Ryan Spencer

 Developer

Share:

[![Twitter](https://www.bitovi.com/hubfs/limbo-generated/_astro/twitter-white.os3xLc3C_Z2nW4or.svg) ](https://twitter.com/intent/tweet?text=) [![Reddit](https://www.bitovi.com/hubfs/limbo-generated/imgs/icons/reddit.png) ](http://reddit.com/submit?url=)

The other day I was uploading a lot of content, including images, to a 3rd party service. I put together all my objects and mapped through them to fire off requests. Nearly immediately, I blew past the API rate limiter, and my requests started failing. Oops.

While this problem isn’t particularly hard, needing to rate limit requests is somewhat uncommon. Since I hadn’t done it in a while, it took me a bit to figure it out.

Let’s work through all my iterations of potential solutions. The code examples are fake and simplified to make it easier to understand the concepts. There is a fully functional React code sandbox at the end of the post, which contains some real code you can use.

## Using Map

Let’s take a look at my first thought, some code using `.map`.

```
import csvData from '../data.csv'
import { post } from '../api/post.ts' // Assume this sends data to our api

function uploadData(data: Array<any>) {
  data.map(datum => post(datum)) // datum is such a weird word
}
```

### What happens here?

The `uploadData` method works through the array of data, running `post` for each item. For our pretend code, the `post` method returns a promise. However, there is nothing in this code that says a request has to wait for the previous request to finish before it runs.

![fireeverythingwevegot](https://www.bitovi.com/hs-fs/hubfs/fireeverythingwevegot.gif?width=550&height=268&name=fireeverythingwevegot.gif)

 

What ends up happening is all requests fire at once, or more correctly, all of the promises start as fast as the map can run. Browsers will limit in-flight requests to a handful at a time and will start the next in the queue as soon as one finishes. If you run this code as a Node script, there will be no limit and all the requests will hit the server in rapid succession. A co-worker and I took out our own staging server this way once, it's called a DoS attack. Hi Bob!

## Wrapping in a Promise

*What if we * `await` * each post, that will work right?*

```
function uploadData(data: Array<any>) {
  data.map(async (datum) => await post(datum))
}
```

Nope. This just wraps each promise in another promise. `Map` still creates all the promises as fast as it can, and the result is the same.

## The Loops that Wait

Enter the ES6 `for..of` loop. [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of)

```
async function uploadData(data: Array<any>) {
  for (const datum of data) {
    await post(datum)
  }
}
```

With some tweaks to the code, promises will run one at a time. A `for..of` one isn't going to work in IE, but we're letting that die, right?

The `while` loop has been around forever and will work with IE6. Probably even earlier versions, but I’m not going to bother looking it up.

```
async function uploadData(data: Array<any>, batchSize = 5) {
  while (data.length) {
    const datum = data.shift() // takes first item, altering the original array
    await post(datum)
  }
}
```

## Too Slow, Let’s Batch

Firing all requests at the same time can overload the server or exceed rate limits, but sending one request at a time takes too long. Let’s look at how we can group our requests into batches.

```
async function uploadData(data: Array<any>, batchSize = 5) {
  while (data.length) {
    await Promise.all(data.splice(0, batchSize).map((datum) => post(datum)));
  }
}
```

In case you were wondering, `.splice` takes a start and end value, then removes and returns those items from the original array. The original array is mutated. If the end value is beyond the length of the array, then it returns up to and including the last item.

That code is pretty simple, but there is still room for improvement. When a batch runs faster than expected, rate limits can still be exceeded.

![wreckitralphbunny](https://www.bitovi.com/hs-fs/hubfs/wreckitralphbunny.gif?width=480&height=201&name=wreckitralphbunny.gif)

## Introducing Time

If the rate limit is known, let’s say 5 requests per second, it is possible to introduce that time into the code by adding a `setTimeout` to each batch of promises. Because we are wrapping our batches in `Promise.all`, the code won’t move to the next loop until all promises complete, meaning the time given to the `setTimeout` is the minimum length of time that will pass before the next batch starts.

```
async function uploadData(data: Array<any>, batchSize = 5, minTime = 5000) {
  while (data.length) {
    await Promise.all([
      ...data.splice(0, batchSize).map(datum => post(datum)),
      () => new Promise<void>(resolve => setTimeout(resolve, minTime))
    ])
  }
}
```

![preciselyonschedule](https://www.bitovi.com/hs-fs/hubfs/preciselyonschedule.gif?width=340&height=276&name=preciselyonschedule.gif)

 

## Interactive Code Sandbox

Want to see this working in a React project? Check out this [code sandbox](https://codesandbox.io/p/sandbox/lucid-water-v853g6).

## Summary

That’s five different ways to fire off a collection of promises, from the basic “Fire everything!” to the orderly and precise batching with a time restriction. I hope that helps you. Depending on your use case, you probably either want the version that fires promises sequentially or the final version with batching and time-based rate limiting.

I expect to be checking back in on this article a few years from now after I’ve forgotten how all of this works.

### What do you think?

We’d love to hear your thoughts on batched API requests! Join our Community Discord and drop into the `#React` channel to chat with our [React Consulting](https://www.bitovi.com/frontend-javascript-consulting/react-consulting) experts 🙌

[![Join our Discord](https://no-cache.hubspot.com/cta/default/2171535/224f5741-883f-452d-b142-dd575d07685e.png)](https://cta-redirect.hubspot.com/cta/redirect/2171535/224f5741-883f-452d-b142-dd575d07685e)

 

[![Tag for javascript](https://www.bitovi.com/hubfs/limbo/icons/tag.svg) javascript ](https://www.bitovi.com/blog/topic/javascript)

 Previous Post

![Conflict-Free Leadership: The Value of Retrospectives [Part 4]](https://www.bitovi.com/hs-fs/hubfs/Conflict-Free%20Leadership-%20The%20Value%20of%20Retrospectives%20%5BPart%204%5D.png?height=117&name=Conflict-Free%20Leadership-%20The%20Value%20of%20Retrospectives%20%5BPart%204%5D.png) [ Conflict-Free Leadership: The Value of Retrospectives [Part 4] ](https://www.bitovi.com/blog/conflict-free-leadership-value-of-retrospectives)

  

 Next Post

![Conflict-Free Leadership: The Framework in Action [Part 5]](https://www.bitovi.com/hs-fs/hubfs/Conflict-Free%20Leadership-%20The%20Framework%20in%20Action%20%5BPart%205%5D.png?height=117&name=Conflict-Free%20Leadership-%20The%20Framework%20in%20Action%20%5BPart%205%5D.png) [ Conflict-Free Leadership: The Framework in Action [Part 5] ](https://www.bitovi.com/blog/conflict-free-leadership-framework-in-action)

```json
{
  "@context" : "http://schema.org",
  "@type" : "Organization",
  "address" : {
    "@type" : "PostalAddress",
    "addressCountry" : "United States",
    "addressLocality" : "Libertyville",
    "addressRegion" : "IL",
    "postalCode" : "60048",
    "streetAddress" : "1134 Pine Tree Lane "
  },
  "alternateName" : "Bitovi",
  "areaServed" : {
    "@type" : "GeoCircle",
    "geoMidpoint" : {
      "@type" : "GeoCoordinates",
      "latitude" : "41.8781",
      "longitude" : "87.6298"
    },
    "geoRadius" : "5000 km"
  },
  "description" : "Bitovi is a UX, UI design and front-end JavaScript development consulting company",
  "email" : "contact@bitovi.com",
  "image" : "https://www.bitovi.com/hubfs/bitovi-logo-x2.png",
  "logo" : "https://www.bitovi.com/hubfs/bitovi-logo-23-1.svg",
  "mainEntityOfPage" : {
    "@id" : "https://www.bitovi.com/blog/dont-overload-the-api-sequential-batched-promises",
    "@type" : "WebPage",
    "description" : "Learn to juggle JavaScript promises through various throttling methods to avoid overloading the API."
  },
  "naics" : "541511",
  "name" : "Bitovi Web App Consulting",
  "sameAs" : [ "https://www.facebook.com/BitoviLLC/", "https://twitter.com/bitovi", "https://www.linkedin.com/company/bitovi" ],
  "telephone" : "312-620-0386",
  "url" : "http://bitovi.com"
}
```

```json
{
  "@context" : "http://schema.org",
  "@type" : "BlogPosting",
  "author" : {
    "@type" : "Person",
    "name" : "Ryan Spencer"
  },
  "dateModified" : "October 3, 2023, 7:46:17 PM",
  "datePublished" : "2023-05-05 16:20:03",
  "description" : "Learn to juggle JavaScript promises through various throttling methods to avoid overloading the API.",
  "headline" : "Don’t Overload the API: Sequential &amp; Batched Promises",
  "image" : {
    "@type" : "ImageObject",
    "url" : "https://www.bitovi.com/hubfs/Don%E2%80%99t%20Overload%20the%20API-%20Sequential%20&%20Batched%20Promises-1.png"
  },
  "publisher" : {
    "@type" : "Organization",
    "logo" : {
      "@type" : "ImageObject",
      "url" : "https://www.bitovi.com/hubfs/bitovi-logo-23-1.svg"
    },
    "name" : "Bitovi"
  }
}
```