Using Promises in JavaScript: Methods and Concurrency Control
1. Promise Methods Overview
Promise.resolve
Wraps a value into a resolved promise.
Promise.resolve('hello').then(s => console.log(s));
Promise.reject
Wraps a value into a rejected promise.
Promise.reject(reason).catch(e => console.log(e));
Promise.all
Waits for all promises to resolve. Rejects immediately if any promise rejects.
Promise.allSettled
Waits for all promises to complete (resolve or reject). Never short-circuits. Returns an array of objects with status and either value or reason.
// [
// { status: 'fulfilled', value: 42 },
// { status: 'rejected', reason: -1 }
// ]
Promise.race
Resolves or rejects as soon as one of the promises settles (first to complete).
Promise.any
Resolves as soon as one promise fulfills. Rejects only if all promises reject.
Promise.try (Stage 3 proposal)
Wraps a synchronous function to execute synchronously or asynchronously depending on the function.
const f = () => console.log('now');
Promise.try(f)
console.log('next');
// now
// next
2. Concurrency Control for Multiple Requests
Approach with Promise.allSettled (no interruption)
Method 1: Chunked slices
const root = 'http://localhost:8000';
const request = async (url) => {
const res = await fetch(root + '/login');
const text = await res.text();
console.log(`res=${text}==${url}`);
};
const total = 21;
const maxConcurrent = 5;
const allUrls = new Array(total).fill('').map((_, i) => `user${i}`);
const chunks = [];
async function runAll() {
for (let i = 0; i < Math.ceil(total / maxConcurrent); i++) {
chunks.push(allUrls.slice(i * maxConcurrent, (i + 1) * maxConcurrent));
}
for (const chunk of chunks) {
const promises = chunk.map(url => request(url));
await Promise.allSettled(promises);
}
}
Method 2: Recursive drain
const createPromise = (id) => () => new Promise(resolve => {
console.log(`Starting ${id}`);
setTimeout(() => resolve(id), 1000);
});
const allTasks = [createPromise(1), createPromise(2), createPromise(3), createPromise(4), createPromise(5)];
const max = 2;
const inFlight = [];
function addTask(task) {
const p = task();
const index = inFlight.length;
inFlight.push(p);
p.then(() => {
inFlight.splice(index, 1);
});
return p;
}
function run() {
if (allTasks.length === 0 && inFlight.length === 0) return;
while (inFlight.length < max && allTasks.length > 0) {
addTask(allTasks.shift());
}
Promise.allSettled(inFlight).then(() => run());
}
run();

Analysis: Promise.allSettled processes chunks of up to maxConcurrent requests in sequence. Each chunk completes before the next starts, ensuring controlled concurrency.
Approach with Promise.race (fastest first)
Method 1: Race with dynamic addition
const root = 'http://localhost:8000';
const request = (url) => fetch(root + '/login').then(res => res.text()).then(text => console.log(`res=${text}==${url}`));
const max = 5;
const allUrls = ['url1', 'url2', ...]; // 21 items
const inFlight = [];
function startNext() {
if (allUrls.length === 0) return;
const url = allUrls.shift();
const p = request(url);
const index = inFlight.length;
inFlight.push(p);
p.then(() => {
inFlight.splice(index, 1);
startNext();
});
}
for (let i = 0; i < max; i++) {
startNext();
}
// Wait for all to complete? Not shown but can use Promise.all on remaining inFlight
Method 2: Recursive race
const createPromise = (id) => () => new Promise(resolve => {
console.log(`Starting ${id}`);
setTimeout(() => resolve(id), Math.random() * 2000);
});
const allTasks = [createPromise(1), createPromise(2), createPromise(3), createPromise(4), createPromise(5)];
const max = 2;
const inFlight = [];
function addTask(task) {
const p = task();
const index = inFlight.length;
inFlight.push(p);
p.then(() => {
inFlight.splice(index, 1);
});
return p;
}
function run() {
if (allTasks.length === 0 && inFlight.length === 0) return;
while (inFlight.length < max && allTasks.length > 0) {
addTask(allTasks.shift());
}
Promise.race(inFlight).then(() => run());
}
// Fill initial batch
for (let i = 0; i < max; i++) {
addTask(allTasks.shift());
}
run();

Analysis: Promise.race triggers a new request as soon as any in-flight request completes. This ensures the fastest requests finish first and the pool remains at max concurrency until all are done.
Comparison
- Both methods control concurrency to reduce server load.
Promise.allis not recommended because it aborts on first rejection; usePromise.allSettledfor robustness.Promise.racecan display data faster by prioritizing quick responses.Promise.raceprovides smoother, more responsive concurrency management.