Condivisione della tecnologia

Come il front-end controlla le richieste simultanee

2024-07-12

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

Quando è necessario che il front-end controlli le richieste simultanee Quando sono necessarie più volte per richiedere i dati richiesti? Ad esempio, se l'interfaccia restituisce molti dati contemporaneamente, il browser si bloccherà nel rendering o addirittura si bloccherà. In questo momento, possiamo inviare 6 richieste contemporaneamente in batch, in modo da evitare blocchi o arresti anomali.

Quindi, come fa il front-end a controllare le richieste simultanee?

Idee chiave per il controllo front-end delle richieste simultanee

Ad esempio, se ci sono 20 richieste, devi seguire 3 Dopo che il primo gruppo avrà completato la richiesta, verrà richiesta la richiesta al secondo gruppo e così via.

L'idea chiave è memorizzare il metodo di richiesta e i parametri di richiesta in un array, quindi richiederne 3 alla volta e quindi richiedere i 3 successivi una volta completata la richiesta. Dopo la restituzione di ciascun gruppo di richieste, i risultati vengono salvati e, dopo la restituzione di tutte le richieste, vengono restituiti tutti i risultati.

progettazione dell'API

pControl : controller delle richieste simultanee, che supera il numero massimo di simultanee;
add : Aggiungi richiesta e parametri;
start : Avvia richiesta, promessa di restituzione, passaggio dopo il completamento della richiesta.then Ottieni tutti i risultati;

Codice

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

Interpretazione del codice chiave

  • pControl: Questa funzione restituisce un oggetto contenente add Estart Due metodi,add Utilizzato per aggiungere attività e parametri,start Utilizzato per avviare la richiesta, è una chiusura.

  • runAllTasks: restituisce apromise, e poi dentronew PromiseEseguito internamente in modo ricorsivorunTask, runTask passaPromise.all Esegui richieste simultanee inPromise.all().then() chiamare di nuovorunTask, implementare un set di richieste da restituire e quindi eseguire il secondo set di richieste.

La chiave per realizzare l’attesa di gruppo è .then chiamata ricorsiva.

Pensiero: è possibile implementare runAllTasks utilizzando un loop?

Sì, è necessario utilizzarlo 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

Non può essere utilizzato nel ciclo for .then , altrimenti il ​​ciclo successivo non attenderà il ciclo precedente.

utilizzo for of Implementazione 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

Come utilizzare loop e Promise insieme?

forwhilefor...of Per le strutture di loop imperative, se si desidera ottenere l'effetto di attesa nel loop, è necessario utilizzareasync wrapper di funzioni nel cicloawait ,Fuori servizio.then

forEachmapfilter Strutture di loop funzionali come queste non supportano gli effetti di attesa, poiché queste strutture di loop funzionali sono sincrone e non supportano l'attesa.

async E循环 + await Combinato per ottenere l'effetto di attesa tra i cicli.

promise.then E递归 Combinato per ottenere l'effetto di attesa tra i cicli.

Migliora l'API per facilitarne l'utilizzo

  1. Imposta i parametri predefiniti: daipControlImpostare un valore predefinito adatto, impostare su6, poiché si trova lo stesso nome di dominio, le richieste simultanee del browser sono 6.
  2. start to callback: Attraverso la callback è possibile ottenere i risultati delle richieste di ciascun gruppo e conoscere il numero di richieste attualmente completate.

Questi due miglioramenti sono semplici. Diamo prima un'occhiata all'utilizzo:

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

Cosa fa la richiamata iniziale?

È conveniente per gli utenti ottenere i risultati delle richieste simultanee correnti e calcolare l'avanzamento del completamento.

Incapsulare le funzioni di cui sopra in p-control rilascio del pacchetto npm

npm: controllo p

accessibile npm i p-control Scarica e utilizza.

riepilogo

  • .then Combinato con la ricorsione per ottenere l'attesa tra attività asincrone;
  • forwhileAspetta il ciclo easync + awaitUtilizzato in combinazione per ottenere l'attesa tra attività asincrone;
  • utilizzoPromise.allImplementa più attività asincrone da eseguire contemporaneamente.