Controlling Concurrent Requests in JavaScript with Promise.all, Promise.race, and async/await
This article explains how to manage multiple asynchronous HTTP requests in modern web development by using Promise.all, Promise.race, async/await, manual counters, and third‑party libraries, providing complete code examples and best‑practice recommendations for limiting concurrency and improving application performance.
Opening
In modern web development, asynchronous requests are essential, but handling many requests simultaneously can cause conflicts and chaos; this article explores how to control concurrent requests using Promise methods.
JavaScript offers several ways to manage asynchronous concurrency, such as Promise.all() , Promise.race() , and async/await . Below is an example that uses Promise.all() to limit concurrency.
Promise.all()
const urls = ["url1", "url2", ... ,"url100"];
const maxConcurrentNum = 10; // maximum concurrent requests
// Split array into chunks, each chunk size = maxConcurrentNum
function chunk(arr, chunk) {
let result = [];
for (let i = 0, len = arr.length; i < len; i += chunk) {
result.push(arr.slice(i, i + chunk));
}
return result;
}
// Async request function
function fetchUrl(url) {
return new Promise((resolve, reject) => {
fetch(url)
.then(res => resolve(res))
.catch(err => reject(err));
});
}
// Chunk the URLs
const chunkedUrls = chunk(urls, maxConcurrentNum);
(async function () {
try {
for (let urls of chunkedUrls) {
const promises = urls.map(url => fetchUrl(url));
const results = await Promise.all(promises);
console.log('results:', results);
}
} catch (err) {
console.error(err);
}
})();The code splits the URL list into equally sized sub‑arrays and launches at most maxConcurrentNum requests at a time; once a batch finishes, the next batch starts, effectively throttling concurrency.
Promise.race()
The following example shows how to use Promise.race() to obtain the fastest response among many requests.
const promiselist = [];
for (let i = 0; i < 100; i++) {
const promise = fetch(`https://example.com/data${i}.json`);
promiselist.push(promise);
}
Promise.race(promiselist)
.then(response => {
// handle the fastest response here
})
.catch(error => {
console.error(error);
});async/await
Using async/await together with Promise.all() provides a clear way to fire many requests and wait for all of them.
async function getData() {
const promiselist = [];
for (let i = 0; i < 100; i++) {
const promise = fetch(`https://example.com/data${i}.json`);
promiselist.push(promise);
}
const responses = await Promise.all(promiselist);
for (const response of responses) {
// handle each response here
}
}
getData().catch(error => {
console.error(error);
});If only the quickest result is needed, wrap Promise.race() inside an async function, similar to the Promise.all() approach.
Beyond these built‑in methods, other techniques exist for concurrency control:
Manual counter : Use a variable to track active requests and pause new ones until a slot frees up. Example: function getData() { const limit = 5; // maximum concurrent requests const dataUrls = [ 'https://example.com/data1.json', 'https://example.com/data2.json', 'https://example.com/data3.json', 'https://example.com/data4.json', 'https://example.com/data5.json', 'https://example.com/data6.json' ]; let counter = 0; const getDataPromise = dataUrl => { return new Promise((resolve, reject) => { fetch(dataUrl) .then(response => { counter--; resolve(response); }) .catch(error => { counter--; reject(error); }); }); }; const getDataPromises = dataUrls.map(dataUrl => { if (counter < limit) { counter++; return getDataPromise(dataUrl); } else { return new Promise(resolve => { const interval = setInterval(() => { if (counter < limit) { counter++; clearInterval(interval); resolve(getDataPromise(dataUrl)); } }, 100); }); } }); Promise.all(getDataPromises) .then(responses => { for (const response of responses) { // handle each response here } }) .catch(error => { console.error(error); }); } getData(); This code manually tracks the number of active requests and uses setInterval to wait for an available slot.
Third‑party libraries : Libraries such as async.js and p-limit provide ready‑made concurrency utilities. p-limit is a lightweight tool specifically for limiting Promise concurrency; see its documentation for usage examples.
Summary
By mastering Promise techniques—including Promise.all , Promise.race , async/await , manual counters, and dedicated libraries—you can efficiently control concurrent requests, making your web applications smoother and more responsive.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.