Partage de technologie

Comment le front-end contrôle les requêtes simultanées

2024-07-12

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

Quand le front-end doit-il contrôler les requêtes simultanées ? Lorsqu'il faut plusieurs fois pour demander les données requises. Par exemple, si l'interface renvoie une grande quantité de données à la fois, le navigateur sera bloqué dans le rendu ou même plantera. À ce moment-là, nous pouvons émettre 6 requêtes en même temps par lots, afin d'éviter un blocage ou un crash. .

Alors, comment le front-end contrôle-t-il les requêtes simultanées ?

Idées clés pour le contrôle frontal des requêtes simultanées

Par exemple, s'il y a 20 demandes, vous devez suivre 3 Une fois que le premier groupe a terminé la demande, le deuxième groupe sera demandé, et ainsi de suite.

L'idée clé est de stocker la méthode de requête et les paramètres de requête dans un tableau, puis d'en demander 3 à la fois, puis de demander les 3 suivants une fois la requête terminée. Après le retour de chaque groupe de requêtes, les résultats sont enregistrés et une fois que toutes les requêtes sont renvoyées, tous les résultats sont renvoyés.

conception d'API

pControl : Contrôleur de requêtes simultanées, transmettant le nombre maximum de simultanéités ;
add : Ajouter une requête et des paramètres ;
start  : Démarrer la demande, renvoyer la promesse, réussir une fois la demande terminée.then Obtenez tous les résultats ;

Code

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

Interprétation du code clé

  • pControl: Cette fonction renvoie un objet contenant add etstart Deux méthodes,add Utilisé pour ajouter des tâches et des paramètres,start Utilisé pour démarrer la requête, c'est une fermeture.

  • runAllTasks: renvoie unpromise, puis dansnew PromiseExécuté en interne de manière récursiverunTask, runTask réussitPromise.all Exécuter des requêtes simultanées dansPromise.all().then() appelle encore une foisrunTask, implémentez un ensemble de requêtes à renvoyer, puis exécutez le deuxième ensemble de requêtes.

La clé pour réaliser l'attente de groupe est de .then appel récursif.

Réflexion : runAllTasks peut-il être implémenté à l'aide d'une boucle ?

Oui, il faut l'utiliser 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

Ne peut pas être utilisé dans une boucle for .then , sinon la boucle suivante n'attendra pas la boucle précédente.

utiliser for of Implémentation itérative :

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

Comment utiliser les boucles et Promise ensemble ?

forwhilefor...of Pour les structures de boucle impératives, si vous souhaitez obtenir l'effet d'attente dans la boucle, vous devez utiliserasync wrapper de fonction dans la boucleawait ,Hors service.then

forEachmapfilter Les structures de boucle fonctionnelle telles que celles-ci ne prennent pas en charge les effets d'attente, car ces structures de boucle fonctionnelle sont synchrones et ne prennent pas en charge l'attente.

async et循环 + await Combiné pour obtenir l'effet d'attente entre les boucles.

promise.then et递归 Combiné pour obtenir l'effet d'attente entre les boucles.

Améliorer l'API pour la rendre plus facile à utiliser

  1. Définir les paramètres par défaut : donnerpControlDéfinissez une valeur par défaut appropriée, définie sur6, comme il s'agit du même nom de domaine, les requêtes simultanées du navigateur sont au nombre de 6.
  2. commencer à rappeler : grâce au rappel, vous pouvez obtenir les résultats des demandes de chaque groupe et connaître le nombre de demandes actuellement terminées.

Ces deux améliorations sont simples. Voyons d'abord l'utilisation :

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

À quoi sert le rappel de démarrage ?

Il est pratique pour les utilisateurs d'obtenir les résultats des demandes simultanées en cours et de calculer la progression.

Encapsulez les fonctions ci-dessus dans p-control version du paquet npm

npm: p-contrôle

accessible npm i p-control Téléchargez et utilisez.

résumé

  • .then Combiné avec la récursivité pour réaliser l'attente entre les tâches asynchrones ;
  • forwhileAttendez la boucle etasync + awaitUtilisé en combinaison pour réaliser une attente entre des tâches asynchrones ;
  • utiliserPromise.allImplémentez plusieurs tâches asynchrones à exécuter simultanément.