Как реализовать аналог Promise.all() c очередью?

Как реализовать Promise.all() с динамической очередью фиксированной ёмкости,
то есть чтобы в отдельный момент времени в обработке находились, например, до 10 параллельных запросов?

const promises = Array(10)
  .fill(null)
  .map((item, i) => new Promise((resolve) => resolve(i)))

const arr = await PromiseAllByChanck.all(promises , 2)

Другими словами, чтобы запросы выполнялись сегментами массива запросов, поблочно?


Ответы (1 шт):

Автор решения: Артём Ионаш

Дело в том, что каждый созданный Promise по сути и является запросом, отправленным на исполнение, поэтому в искомом решении не может быть такой сущности как массив "обещаний" promises.

То есть они все создаются и "отправляются на исполнение" прямо с этой строки:

const promises = Array(10)
  .fill(null)
  .map((item, i) => new Promise((resolve) => resolve(i)))
const five = await promises[5] // 5

Чтобы предотвратить это, можно "экранировать" их, точнее преобразовать в асинхронные функции:

const tasks = Array(10)
  .fill(null)
  .map((item, i) => async () => i)
const five = await tasks[5]() // 5

Тогда аналог Promise.all() со внутренней очередью будет выглядеть примерно так:

class ExecutorPool {
  constructor(size) {
    if (size < 1)
      throw new Error('[ExecutorPool] Размер очереди меньше единицы.')
    this._size = size
  }

  async execute(tasks) {
    const results = Array(tasks.length)
    const executors = Array(this._size)
      .fill(null)
      .map((e, executorId) => async (taskPool) => {
        while (true) {
          const task = taskPool.pop()
          if (!task) break

          const index = taskPool.length
          results[index] = await task()
          console.log(JSON.stringify({ taskId: results[index], executorId }))
        }
      })
    const taskPool = [...tasks]
    await Promise.all(executors.map((executor) => executor(taskPool)))
    return results
  }
}

new Promise(async () => {
  const tasks = Array(10)
    .fill(null)
    .map((item, i) => async () => i)
  const ePool = new ExecutorPool(2)
  const results = await ePool.execute(tasks)
  console.log(JSON.stringify(results))
})

→ Ссылка