Compartir tecnología

Cómo controla el front-end las solicitudes simultáneas

2024-07-12

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

¿Cuándo necesita la interfaz controlar las solicitudes simultáneas? ¿Cuándo se necesitan varias veces para solicitar los datos requeridos? Por ejemplo, si la interfaz devuelve una gran cantidad de datos a la vez, el navegador se bloqueará en el procesamiento o incluso fallará. En este momento, podemos emitir 6 solicitudes al mismo tiempo en lotes para evitar bloqueos o fallas. .

Entonces, ¿cómo controla el front-end las solicitudes concurrentes?

Ideas clave para el control front-end de solicitudes simultáneas

Por ejemplo, si hay 20 solicitudes, debes seguir 3 Una vez que el primer grupo complete la solicitud, se solicitará al segundo grupo, y así sucesivamente.

La idea clave es almacenar el método de solicitud y los parámetros de solicitud en una matriz, luego solicitar 3 a la vez y luego solicitar los 3 siguientes una vez completada la solicitud. Después de que regresa cada grupo de solicitudes, se guardan los resultados y, una vez que se devuelven todas las solicitudes, se devuelven todos los resultados.

diseño de API

pControl : Controlador de solicitudes concurrentes, que pasa el número máximo de concurrencias;
add : Agregar solicitud y parámetros;
start : Iniciar solicitud, devolver promesa, pasar después de completar la solicitud.then Obtenga todos los 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

Interpretación del código clave

  • pControl: Esta función devuelve un objeto que contiene add ystart Dos métodos,add Se utiliza para agregar tareas y parámetros,start Se utiliza para iniciar la solicitud, es un cierre.

  • runAllTasks: devuelve unpromise, y luego ennew PromiseEjecutado internamente de forma recursivarunTask, ejecutarPases de tareaPromise.all Ejecutar solicitudes simultáneas enPromise.all().then() llama de nuevorunTask, implemente un conjunto de solicitudes para devolver y luego ejecute el segundo conjunto de solicitudes.

La clave para lograr la espera grupal es .then llamada recursiva.

Pensando: ¿Se puede implementar runAllTasks mediante un bucle?

Sí, necesito 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

No se puede utilizar en el bucle for .then ; de lo contrario, el siguiente bucle no esperará al bucle anterior.

usar for of Implementación 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

¿Cómo usar loops y Promise juntos?

forwhilefor...of Para estructuras de bucle imperativo, si desea lograr el efecto de espera en el bucle, debe usarasync contenedor de funciones en bucleawait ,Fuera de servicio.then

forEachmapfilter Las estructuras de bucle funcionales como éstas no admiten efectos de espera, porque estas estructuras de bucle funcionales son síncronas y no admiten la espera.

async y循环 + await Combinado para lograr el efecto de espera entre bucles.

promise.then y递归 Combinado para lograr el efecto de espera entre bucles.

Mejorar la API para que sea más fácil de usar.

  1. Establecer parámetros predeterminados: darpControlEstablezca un valor predeterminado adecuado, establecido en6Debido a que tiene el mismo nombre de dominio, las solicitudes simultáneas del navegador son 6.
  2. comenzar a devolver la llamada: a través de la devolución de llamada, puede obtener los resultados de la solicitud de cada grupo y conocer la cantidad de solicitudes completadas actualmente.

Estas dos mejoras son simples. Veamos primero el 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

¿Qué hace la devolución de llamada de inicio?

Es conveniente para los usuarios obtener los resultados de las solicitudes simultáneas actuales y calcular el progreso de finalización.

Encapsule las funciones anteriores en p-control lanzamiento del paquete npm

npm: control p

accesible npm i p-control Descargar y usar.

resumen

  • .then Combinado con recursividad para lograr la espera entre tareas asincrónicas;
  • forwhileEspere el bucle yasync + awaitSe utiliza en combinación para lograr la espera entre tareas asincrónicas;
  • usarPromise.allImplemente múltiples tareas asincrónicas para ejecutarlas simultáneamente.