Recorrido asíncrono de JS, ¿realmente puedes escribirlo?

Llega oferta, cava amigos a recoger! Estoy participando en el evento de registro de reclutamiento de primavera de 2022, haga clic para ver los detalles del evento .

A veces necesitamos atravesar los elementos de la matriz y pasarlos a la función asíncrona para su ejecución. El método de escritura asíncrona es fácil de escribir mal. Echemos un vistazo a los puntos propensos a errores.

Supongamos que tenemos un método asíncrono sleepPromise de la forma:

function sleepPromise(msg, t{
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(`promise-${msg} ${t}s`);
    }, t * 1000);
  })
}
复制代码

Para facilitar la demostración, el método de suspensión en forma de promesa se escribe utilizando setTimeout. El t entrante es el tiempo de ejecución del retraso y msg es el contenido de la información.

En el desarrollo real, el método asíncrono puede ser pasar la identificación de amigo del usuario para buscar en la base de datos y obtener información simple sobre amigos.

Supongamos que necesitamos escribir una implementación de conveniencia asíncrona debajo de la posición comentada en el código a continuación.

async function loopAsync({
  console.log('[start]');
  const curTime = Date.now();

  const tasks = [
    ['a'3],
    ['b'1],
    ['c'2],
  ];
  
  // 下面写遍历 tasks 传入到异步方法的实现

  console.log(`[end] duration ${((Date.now() - curTime) / 1000).toFixed(2)}s`);
}
复制代码

Ortografía incorrecta: forEach

Por lo general, el front-end usa forEach tan pronto como ve que se va a atravesar la matriz. Si no eres lo suficientemente sofisticado, podrías escribir una implementación como esta:

// 错误的 forEach 写法
tasks.forEach(async task => {
  const [ msg, t ] = task;
  const m = await sleepPromise(msg, t);
  console.log(m);
})
复制代码

La salida es;

[start]
[end] duration 0.00s
promise-b 1s
promise-c 2s
promise-a 3s
复制代码

Esta forma de escribir no es correcta, de hecho, el recorrido se escribe como sincronización.

¿Cuál es el problema? Debido a que forEach en sí mismo no admite la escritura asincrónica, no es válido si agrega la palabra clave await delante del método forEach, porque no tiene lógica asincrónica.

forEach es una API de ES5, mucho más antigua que ES6 Promise. Por compatibilidad con versiones anteriores, forEach tampoco admitirá el procesamiento asíncrono en el futuro.

Por lo tanto, la ejecución de forEach no bloqueará el código después de loopAsync, por lo que provocará una falla de bloqueo y la salida primero  [end].

Escritura en serie: bucle for

// 串行写法
for (const task of tasks) {
  const [ msg, t ] = task;
  const m = await sleepPromise(msg, t);
  console.log(m);
}
复制代码

Usando el método ordinario de escritura de bucle for, la función externa de await sigue siendo el método loopAysnc, y el código de bloqueo se puede guardar correctamente.

Pero el problema aquí es que la ejecución de estos métodos asincrónicos es  serial  . Se puede observar que se ejecutaron un total de 6 s.

[start]
promise-a 3s
promise-b 1s
promise-c 2s
[end] duration 6.01s
复制代码

Si nuestras solicitudes tienen dependencias secuenciales, no hay problema.

Pero si nuestro escenario es buscar el nombre de usuario correspondiente de la base de datos en función de la matriz de ID de usuario, nuestra complejidad de tiempo  O(n) no es razonable.

En este punto, debemos reescribirlo en  asincronía paralela  y también asegurarnos de que toda la asincronía se ejecute antes de ejecutar el siguiente paso. podemos  Promise.all()usar

Implementación paralela: Promise.all

// 并行写法
const taskPromises = tasks.map(task => {
  const [ msg, t ] = task;
  return sleepPromise(msg, t).then(m => {
    console.log(m);
  });
});
await Promise.all(taskPromises);
复制代码

Primero, necesitamos generar la matriz de objetos de promesa correspondiente basada en la matriz de tareas y luego pasarla al método Promise.all para su ejecución.

De esta forma, estos métodos asincrónicos se ejecutan simultáneamente. Cuando se completa toda la ejecución asincrónica, el código se ejecuta hacia abajo.

La salida es la siguiente:

[start]
promise-b 1s
promise-c 2s
promise-a 3s
[end] duration 3.00s
复制代码

Se acabó en 3 segundos, demasiado fuerte.

volver a para cada uno

Como se mencionó anteriormente, la capa inferior de forEach no implementa el procesamiento asíncrono, lo que conduce a una falla de bloqueo, por lo que también podríamos implementar un forEach simple que admita el procesamiento asíncrono.

Implementación paralela:

async function forEach(arr, fn{
  const fns = [];
  for (let i = 0; i < arr.length; i++) {
    const item = arr[i];
    fns.push(fn(item, i, arr));
  }
  await Promise.all(fns);
}
复制代码

Implementación en serie:

async function forEach(arr, fn{
  for (let i = 0; i < arr.length; i++) {
    const item = arr[i];
    await fn(item, i, arr);
  }
}
复制代码

uso:

await forEach(tasks, async task => {
  const [ msg, t ] = task;
  const m = await sleepPromise(msg, t);
  console.log(m);
})
复制代码

Resumir

resuma brevemente.

En términos generales, usamos el método asíncrono de Promise.all para ejecutar en paralelo, que a menudo se usa en el escenario donde la base de datos busca datos correspondientes a algunas identificaciones.

El método de escritura en serie del bucle for es adecuado para múltiples situaciones asincrónicas con dependencias, como encontrar la referencia final.

forEach es un error tipográfico puro, a menos que no sea necesario usar async/await.

Soy el hermano sandía de front-end, centrado en compartir el conocimiento de front-end, bienvenido a seguirme.

Supongo que te gusta

Origin juejin.im/post/7078695117967589406
Recomendado
Clasificación