Контроллер количества одновременно исполняемых асинхронных задач

Запустите не более limit работников, каждый последовательно берёт очередную задачу. Результат или ошибку записывайте по исходному индексу, не останавливая остальных.

Короткий ответ

Что ответить на собеседовании

Запустите не более limit работников, каждый последовательно берёт очередную задачу. Результат или ошибку записывайте по исходному индексу, не останавливая остальных.

Подробный разбор

Ответ с пояснениями

Условие

Реализуйте функцию runWithConcurrencyLimit(tasks, limit), которая принимает массив функций‑запросов tasks и целое число limit, задающее максимальное количество задач, исполняющихся одновременно.

Каждая функция из массива tasks должна возвращать Promise. Функция runWithConcurrencyLimit обязана запускать задачи, соблюдая указанный порог limit на количество одновременно работающих промисов. В результате она должна вернуть Promise, который разрешается массивом результатов в том же порядке, что и исходный массив tasks. Если какая‑то задача завершится с ошибкой, её ошибка должна быть сохранена в результирующем массиве под тем же индексом, при этом выполнение остальных задач не должно прерываться.

/*
Пример использования:  
const tasks = [  
  () => new Promise(resolve => setTimeout(() => resolve(1), 2000)),  
  () => new Promise(resolve => setTimeout(() => resolve(2), 2000)),  
  () => new Promise((_, reject) => setTimeout(() => reject('Error in task 3'), 1000)),  
  () => new Promise(resolve => setTimeout(() => resolve(4), 4000)),  
]  
*/

runWithConcurrencyLimit(tasks, 2)  
.then(results => console.log(results))  
.catch(err => console.error(err));  

// Ожидаемый вывод после примерно 7 секунд:  
// [1, 2, 'Error in task 3', 4]  

// limit always positive (>= 1)  
function runWithConcurrencyLimit(tasks, limit) {}

Ответ

Запустите не более limit работников, каждый последовательно берёт очередную задачу. Результат или ошибку записывайте по исходному индексу, не останавливая остальных.

async function runWithConcurrencyLimit(tasks, limit) {
  if (!Number.isInteger(limit) || limit < 1) throw new RangeError('Некорректный limit');
  const result = Array(tasks.length);
  let next = 0;
  async function worker() {
    while (next < tasks.length) {
      const index = next++;
      try { result[index] = await tasks[index](); }
      catch (error) { result[index] = error; }
    }
  }
  await Promise.all(Array.from({ length: Math.min(limit, tasks.length) }, worker));
  return result;
}

Синхронное исключение задачи тоже сохраняется. Для примера идеальная длительность около 6 секунд, не 7: вторая задача освобождает слот на второй секунде и четвёртая заканчивает на шестой. Формат из условия не отличает успешное значение 'Error' от ошибки 'Error'; при возможности лучше возвращать tagged-результаты со status.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее