Compartilhamento de tecnologia

Como o front-end controla solicitações simultâneas

2024-07-12

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

Quando o front-end precisa controlar solicitações simultâneas? Quando são necessárias várias vezes para solicitar os dados necessários? Por exemplo, se a interface retornar uma grande quantidade de dados de uma vez, o navegador ficará travado na renderização ou até mesmo travará. Nesse momento, podemos emitir 6 solicitações ao mesmo tempo em lotes, para evitar travamentos ou travamentos. .

Então, como o front-end controla as solicitações simultâneas?

Ideias-chave para controle front-end de solicitações simultâneas

Por exemplo, se houver 20 solicitações, você precisará seguir 3 Após o primeiro grupo concluir a solicitação, o segundo grupo será solicitado e assim sucessivamente.

A ideia principal é armazenar o método de solicitação e os parâmetros de solicitação em uma matriz, solicitar 3 de cada vez e solicitar os próximos 3 após a conclusão da solicitação. Após o retorno de cada grupo de solicitações, os resultados são salvos e, após o retorno de todas as solicitações, todos os resultados são retornados.

design de API

pControl : Controlador de solicitações simultâneas, passando o número máximo de simultaneidades;
add : Adicionar solicitação e parâmetros;
start : Iniciar solicitação, retornar promessa, passar após a conclusão da solicitação.then Obtenha todos os resultados;

Código

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

Interpretação do código-chave

  • pControl: Esta função retorna um objeto contendo add estart Dois métodos,add Usado para adicionar tarefas e parâmetros,start Usado para iniciar a solicitação, é um encerramento.

  • runAllTasks: retorna umpromise, e depois emnew PromiseExecutado internamente recursivamenterunTask, runTask passaPromise.all Executar solicitações simultâneas emPromise.all().then() ligue novamenterunTask, implemente um conjunto de solicitações para retornar e, em seguida, execute o segundo conjunto de solicitações.

A chave para concretizar a espera em grupo é .then chamada recursiva.

Pensando: runAllTasks pode ser implementado usando um loop?

Sim, preciso usar 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

Não pode ser usado em loop for .then , caso contrário, o próximo loop não aguardará o loop anterior.

usar for of Implementação iterativa:

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

Como usar loops e Promise juntos?

forwhilefor...of Para estruturas de loop imperativas, se você deseja obter o efeito de espera no loop, você deve usarasync wrapper de função em loopawait ,Fora de serviço.then

forEachmapfilter Estruturas de loop funcional como essas não suportam efeitos de espera, porque essas estruturas de loop funcional são síncronas e não suportam espera.

async e循环 + await Combinado para obter o efeito de espera entre loops.

promise.then e递归 Combinado para obter o efeito de espera entre loops.

Melhore a API para torná-la mais fácil de usar

  1. Definir parâmetros padrão: darpControlDefina um valor padrão adequado, definido como6, como o mesmo nome de domínio está inserido, as solicitações simultâneas do navegador são 6.
  2. iniciar o retorno de chamada: por meio do retorno de chamada, você pode obter os resultados da solicitação de cada grupo e saber a quantidade de solicitações concluídas no momento.

Essas duas melhorias são simples. Vejamos primeiro o uso:

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

O que o retorno de chamada inicial faz?

É conveniente para os usuários obterem os resultados das solicitações simultâneas atuais e calcularem o progresso da conclusão.

Encapsule as funções acima em p-control lançamento do pacote npm

npm: p-control

acessível npm i p-control Baixe e use.

resumo

  • .then Combinado com recursão para conseguir espera entre tarefas assíncronas;
  • forwhileAguarde o loop easync + awaitUsado em combinação para obter espera entre tarefas assíncronas;
  • usarPromise.allImplemente várias tarefas assíncronas para serem executadas simultaneamente.