Обмен технологиями

Как интерфейс контролирует одновременные запросы

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Когда интерфейсу необходимо контролировать одновременные запросы? Когда требуется несколько раз для запроса необходимых данных? Например, если интерфейс возвращает много данных одновременно, браузер зависнет при рендеринге или даже выйдет из строя. В это время мы можем отправлять 6 запросов одновременно пакетами, чтобы избежать зависания или сбоя.

Так как же интерфейс контролирует одновременные запросы?

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

Например, если запросов 20, нужно следовать 3 После того как первая группа выполнит запрос, будет запрошена вторая группа и так далее.

Основная идея состоит в том, чтобы сохранить метод запроса и параметры запроса в массиве, затем запрашивать по 3 за раз, а затем запрашивать следующие 3 после завершения запроса. После возврата каждой группы запросов результаты сохраняются, а после возврата всех запросов возвращаются все результаты.

API-дизайн

pControl : Контроллер одновременных запросов, передающий максимальное количество параллелизма;
add : Добавить запрос и параметры;
start : Запустить запрос, вернуть обещание, пройти после завершения запроса..then Получить все результаты;

Код

function pControl(limit) {
  const taskQueue = [] // {task: Function, params: any[]}[]
  return {
    add,
    start
  }

  function add(task, params) {
    taskQueue.push({
      task,
      params
    })
  }

  function start() {
    return runAllTasks()
  }

  function runAllTasks() {
    const allResults = []
    return new Promise((resolve) => {

      runTask()

      function runTask() {
        if (taskQueue.length === 0) {
          // 递归结束
          return resolve(allResults)
        }
        const needRunSize = Math.min(taskQueue.length, limit)
        const tasks = taskQueue.splice(0, needRunSize)
        const promises = tasks.map(({
          task,
          params
        }) => task(params))
        Promise.all(promises).then((resList) => {
          allResults.push(...resList)
          // NOTE 递归调用的位置很关键
          runTask()
        })
      }
    })
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

Интерпретация кода ключа

  • pControl: Эта функция возвращает объект, содержащий add иstart Два метода,add Используется для добавления задач и параметров,start Используется для запуска запроса и является замыканием.

  • runAllTasks: возвращаетpromise, а затем вnew PromiseВнутренне выполняется рекурсивноrunTask, runTask проходитPromise.all Выполнять одновременные запросы вPromise.all().then() позвони сноваrunTask, реализовать один набор запросов на возврат, а затем выполнить второй набор запросов.

Ключом к реализации группового ожидания является .then рекурсивный вызов.

Думаем: можно ли реализовать runAllTasks с помощью цикла?

Да, нужно использовать async 和 for 循环 + await

async function runAllTasks2() {
  const allResults = []
  const groupArr = []
  let startIndex = 0
  // 划分分组
  while (startIndex < taskQueue.length) {
    const arr = taskQueue.slice(startIndex, startIndex + limit)
    groupArr.push(arr)
    startIndex += limit
  }
  for (let index = 0; index < groupArr.length; index++) {
    const pList = groupArr[index].map(({
      task,
      params
    }) => task(params))
    const res = await Promise.all(pList)
    allResults.push(...res)
  }
  return allResults
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

Невозможно использовать в цикле for .then , иначе следующий цикл не будет ждать предыдущего цикла.

использовать for of Итеративная реализация:

async function runAllTasks2() {
  const allResults = []
  const groupArr = []
  let startIndex = 0
  // 划分分组
  while (startIndex < taskQueue.length) {
    const arr = taskQueue.slice(startIndex, startIndex + limit)
    groupArr.push(arr)
    startIndex += limit
  }
  // 迭代分组
  const it = groupArr.entries()
  for (const [key, value] of it) {
    const pList = value.map(({
      task,
      params
    }) => task(params))
    const res = await Promise.all(pList)
    allResults.push(...res)
  }
  return allResults
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

Как использовать циклы и Promise вместе?

forwhilefor...of Для структур императивного цикла, если вы хотите добиться эффекта ожидания в цикле, вы должны использоватьasync функция-обертка в циклеawait , Выведен из строя.then

forEachmapfilter Структуры функционального цикла, подобные этим, не поддерживают эффекты ожидания, поскольку эти структуры функционального цикла являются синхронными и не поддерживают ожидание.

async и循环 + await Комбинируется для достижения эффекта ожидания между циклами.

promise.then и递归 Комбинируется для достижения эффекта ожидания между циклами.

Улучшите API, чтобы его было проще использовать.

  1. Установить параметры по умолчанию: датьpControlУстановите подходящее значение по умолчанию, установите6, поскольку используется одно и то же доменное имя, количество одновременных запросов браузера равно 6.
  2. начать обратный вызов: с помощью обратного вызова вы можете получить результаты запроса каждой группы и узнать количество выполненных на данный момент запросов.

Эти два улучшения просты. Давайте сначала посмотрим на использование:

const asyncTaskControl = pControl() // 默认 6 
asyncTaskControl.add(task, params1)
asyncTaskControl.add(task, params2)
// ...
asyncTaskControl.add(task, params10)

asyncTaskControl.start((res, doneSize) => {
  // 获取每组请求的结果 和当前完成了多少请求
  console.log(res) // [{index:number,result:data}] 
  console.log(doneSize)
}).then(allResults => {
  // 所有请求结果
  console.log(allResults)
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

Что делает обратный вызов?

Пользователям удобно получать результаты текущих одновременных запросов и рассчитывать ход выполнения.

Инкапсулируйте вышеуказанные функции в p-control выпуск пакета npm

npm: p-контроль

доступный npm i p-control Скачайте и пользуйтесь.

краткое содержание

  • .then В сочетании с рекурсией для обеспечения ожидания между асинхронными задачами;
  • forwhileПодождите цикла иasync + awaitИспользуется в сочетании для достижения ожидания между асинхронными задачами;
  • использоватьPromise.allРеализуйте несколько асинхронных задач для одновременного выполнения.