Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Using Promises in JavaScript: Methods and Concurrency Control

Tech 1

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();

Promise.allSettled result

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();

Promise.race result

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.all is not recommended because it aborts on first rejection; use Promise.allSettled for robustness.
  • Promise.race can display data faster by prioritizing quick responses.
  • Promise.race provides smoother, more responsive concurrency management.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.