Asynchrones Durchlaufen von JS, können Sie es wirklich schreiben?

Angebot kommt, grab Freunde zum abholen! Ich nehme an der Rekrutierungs-Check-in-Veranstaltung im Frühjahr 2022 teil. Klicken Sie hier, um die Veranstaltungsdetails anzuzeigen .

Manchmal müssen wir die Elemente des Arrays durchlaufen und sie zur Ausführung an die asynchrone Funktion übergeben. Die asynchrone Schreibmethode kann leicht falsch geschrieben werden. Schauen wir uns die fehleranfälligen Punkte an.

Angenommen, wir haben eine asynchrone Methode sleepPromise der Form:

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

Zur Vereinfachung der Demonstration wird die sleep-Methode in Form von Promise unter Verwendung von setTimeout geschrieben. Das ankommende t ist die Verzögerungsausführungszeit und msg ist der Informationsinhalt.

In der tatsächlichen Entwicklung kann das asynchrone Verfahren darin bestehen, die Freundes-ID des Benutzers zu übergeben, um die Datenbank nachzuschlagen und einfache Freundesinformationen zu erhalten.

Angenommen, wir müssen eine asynchrone Convenience-Implementierung unterhalb der kommentierten Position im folgenden Code schreiben.

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`);
}
复制代码

Falsche Schreibweise: forEach

Normalerweise verwendet das Frontend forEach, sobald es sieht, dass das Array durchlaufen werden soll. Wenn Sie nicht anspruchsvoll genug sind, können Sie eine Implementierung wie diese schreiben:

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

Die Ausgabe ist;

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

Diese Schreibweise ist nicht korrekt, tatsächlich wird die Traversierung als Synchronisation geschrieben.

Was ist das Problem? Da forEach selbst kein asynchrones Schreiben unterstützt, ist es ungültig, ob Sie das Schlüsselwort await vor der forEach-Methode hinzufügen, da darin keine asynchrone Logik enthalten ist.

forEach ist eine ES5-API, viel früher als ES6 Promise. Aus Gründen der Abwärtskompatibilität wird forEach in Zukunft auch keine asynchrone Verarbeitung unterstützen.

Daher blockiert die Ausführung von forEach den Code nach loopAsync nicht, sodass es zu einem Blockierungsfehler und einer ersten Ausgabe kommt  [end].

Serielles Schreiben: For-Schleife

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

Bei Verwendung der gewöhnlichen for-Schleifenschreibmethode ist die äußere Funktion von await immer noch die loopAysnc-Methode, und der Blockierungscode kann korrekt gespeichert werden.

Das Problem hierbei ist jedoch, dass die Ausführung dieser asynchronen Methoden  seriell erfolgt  . Es ist ersichtlich, dass insgesamt 6 s ausgeführt wurden.

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

Wenn unsere Anfragen sequentielle Abhängigkeiten haben, ist dies kein Problem.

Aber wenn unser Szenario darin besteht, den entsprechenden Benutzernamen aus der Datenbank basierend auf dem Benutzer-ID-Array nachzuschlagen, ist unsere Zeitkomplexität  O(n) unangemessen.

An diesem Punkt müssen wir es in  parallele  Asynchronität umschreiben und auch sicherstellen, dass die gesamte Asynchronität ausgeführt wird, bevor der nächste Schritt ausgeführt wird. können wir verwenden  Promise.all().

Parallele Implementierung: Promise.all

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

Zuerst müssen wir das entsprechende Promise-Objektarray basierend auf dem Tasks-Array generieren und es dann zur Ausführung an die Promise.all-Methode übergeben.

Auf diese Weise werden diese asynchronen Methoden gleichzeitig ausgeführt. Wenn die gesamte asynchrone Ausführung abgeschlossen ist, wird der Code nach unten ausgeführt.

Die Ausgabe ist wie folgt:

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

Es ist in 3 Sekunden vorbei, zu stark.

zurück zu forEach

Wie bereits erwähnt, implementiert die unterste Schicht von forEach keine asynchrone Verarbeitung, was zu Blockierungsfehlern führt. Daher könnten wir genauso gut ein einfaches forEach implementieren, das asynchron unterstützt.

Parallele Implementierung:

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);
}
复制代码

Serielle Umsetzung:

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

Verwendungszweck:

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

Zusammenfassen

Kurz zusammenfassen.

Im Allgemeinen verwenden wir die asynchrone Methode von Promise.all zur parallelen Ausführung, die häufig in dem Szenario verwendet wird, in dem die Datenbank nach Daten sucht, die einigen IDs entsprechen.

Die serielle Schreibmethode der for-Schleife eignet sich für mehrere asynchrone Situationen mit Abhängigkeiten, wie z. B. das Finden des endgültigen Referrers.

forEach ist ein reiner Tippfehler, es sei denn, es ist nicht notwendig, async/await zu verwenden.

Ich bin der Front-End-Wassermelonenbruder und konzentriere mich auf den Austausch von Front-End-Wissen. Folgen Sie mir gerne.

Ich denke du magst

Origin juejin.im/post/7078695117967589406
Empfohlen
Rangfolge