Run async tasks with a concurrency limit

JavaScript
// Run async jobs with at most `limit` in flight, preserving result order.
async function mapLimit(items, limit, worker) {
  if (limit < 1) throw new RangeError('limit must be >= 1');
  const results = new Array(items.length);
  let nextIndex = 0;

  async function run() {
    while (nextIndex < items.length) {
      const index = nextIndex++;
      results[index] = await worker(items[index], index);
    }
  }

  const workers = Array.from({ length: Math.min(limit, items.length) }, run);
  await Promise.all(workers);
  return results;
}

const ids = [11, 12, 13, 14, 15];
mapLimit(ids, 2, async (id) => {
  await new Promise((resolve) => setTimeout(resolve, 50));
  return id * 2;
}).then((values) => console.log(values)); // [22, 24, 26, 28, 30]