Heim > Artikel > Web-Frontend > Detaillierte Erläuterung der async+await-Synchronisations-/asynchronen Lösung von ES6
Dieser Artikel stellt hauptsächlich die detaillierte Erklärung der asynchronen + wartenden Synchronisierungs-/Asynchronlösung von ES6 vor. In diesem Artikel wird die prägnanteste Methode zum Entsperren von asynchronem Warten und Warten verwendet. Interessierte können mehr darüber erfahren.
Asynchrone Programmierung war JavaScript-Programmierung wichtige Themen. In Bezug auf asynchrone Lösungen entstand in ES6 zunächst Promise basierend auf der Zustandsverwaltung, dann die Generatorfunktion + Co-Funktion und dann die asynchrone + Wait-Lösung von ES7.
Dieser Artikel ist bestrebt, Async + Wait auf prägnanteste Weise zu entsperren.
Mehrere Szenarien der asynchronen Programmierung
Beginnen Sie mit einer häufig gestellten Frage: Wie druckt man die Iterationssequenz asynchron in einer for-Schleife?
Wir können uns leicht vorstellen, Schließungen oder den in ES6 angegebenen Let-Block-Level-Bereich zu verwenden, um diese Frage zu beantworten.
for (let val of [1, 2, 3, 4]) { setTimeout(() => console.log(val),100); } // => 预期结果依次为:1, 2, 3, 4
Was hier beschrieben wird, ist eine Synchronisierung, die gleichmäßig erfolgt und in einer vorgegebenen Reihenfolge in der asynchronen Warteschlange eingereiht wird, um auf die Ausführung zu warten.
Wenn die Asynchronität nicht gleichmäßig erfolgt, ist die Reihenfolge, in der sie in der asynchronen Warteschlange registriert werden, nicht in der richtigen Reihenfolge.
for (let val of [1, 2, 3, 4]) { setTimeout(() => console.log(val), 100 * Math.random()); } // => 实际结果是随机的,依次为:4, 2, 3, 1
Die zurückgegebenen Ergebnisse sind außer Betrieb und unkontrollierbar, was asynchron am realistischsten ist. Eine andere Situation ist jedoch, dass Sie in einer Schleife Folgendes tun sollten, wenn Sie möchten, dass die vorherige asynchrone Ausführung abgeschlossen wird und die nächste asynchrone Ausführung erneut ausgeführt wird.
for (let val of ['a', 'b', 'c', 'd']) { // a 执行完后,进入下一个循环 // 执行 b,依此类推 }
Sind das nicht einfach mehrere asynchrone „Serien“?
Die Methode, asynchrone Vorgänge im Rückruf zu verschachteln und dann zurückzurufen, löst dieses Problem! Alternativ kann das Problem auch durch die Verwendung von Promise + then() zum Verschachteln von Ebenen gelöst werden. Wenn Sie jedoch darauf bestehen, diese Verschachtelungsmethode in einer Schleife zu schreiben, befürchte ich, dass dies große Probleme verursachen wird. Ich frage mich, gibt es einen besseren Weg?
Asynchrone Synchronisationslösung
Stellen Sie sich vor, wenn Sie einen Datenstapel an den Server senden möchten, wird nur der vorherige Stapel erfolgreich gesendet (d. h. der Server gibt a zurück). erfolgreiche Antwort), Erst dann wird der nächste Datenstapel gesendet, andernfalls wird der Versand abgebrochen. Dies ist ein typisches Beispiel für „interdependente asynchrone Operationen in einer for-Schleife“.
Offensichtlich kann dieser „serielle“ Asynchronismus tatsächlich als Synchronisation angesehen werden. Es dauert länger als asynchrones Out-of-Order. Logischerweise möchten wir, dass das Programm asynchron ausgeführt wird, nur um Blockierungen zu „überspringen“ und weniger Zeit zu benötigen. Aber im Gegenteil, wenn wir eine Reihe asynchroner „Serien“ benötigen, wie sollten wir dann gut programmieren?
Für diese „serielle“ Asynchronität kann ES6 dieses Problem leicht lösen.
async function task () { for (let val of [1, 2, 3, 4]) { // await 是要等待响应的 let result = await send(val); if (!result) { break; } } } task();
Im wahrsten Sinne des Wortes ist es dieser Zyklus. Sobald die Ergebnisse vorliegen, wird der nächste Zyklus durchgeführt. Daher wird die Schleife bei jeder Ausführung angehalten („hängengeblieben“), bis die Schleife endet. Diese Codierungsimplementierung beseitigt effektiv das Problem der verschachtelten „Callback-Hölle“ und reduziert kognitive Schwierigkeiten.
Dies ist die Lösung zur Synchronisierung asynchroner Probleme. Wenn Promise in Bezug auf diese Lösung hauptsächlich das Problem asynchroner Rückrufe löst, löst Async + Wait hauptsächlich das Problem der Synchronisierung asynchroner Probleme und verringert die kognitive Belastung durch asynchrone Programmierung.
async + waiting „äußerlich unterschiedlich, aber innen gleich“
Als ich früher mit dieser API in Kontakt kam, habe ich mir die umständliche Dokumentation angeschaut und Ich dachte, dass Async + Await hauptsächlich zur Lösung des Problems verwendet wurde. Asynchrone Probleme sind synchron.
Das ist nicht der Fall. Wie aus dem obigen Beispiel ersichtlich ist: Das Schlüsselwort async deklariert eine asynchrone Funktion. Der Hauptteil dieser asynchronen Funktion enthält eine Zeile mit Warteanweisungen, die benachrichtigen, dass das Verhalten synchron ausgeführt wird, und die angrenzenden Codes oben und unten werden ausgeführt Zeile für Zeile der Reihe nach.
Um diese formale Sache noch einmal zu übersetzen:
1 Nachdem die asynchrone Funktion ausgeführt wurde, wird immer ein Promise-Objekt zurückgegeben
2 ist synchron
Darunter zeigt 1, dass die Task-Methode nach der Ausführung ein Promise-Objekt zurückgibt. Da sie ein Promise zurückgibt, kann man verstehen, dass die Task eine asynchrone Methode ist. Es besteht kein Zweifel, dass es so verwendet wird:
task().then((val) => {alert(val)}) .then((val) => {alert(val)})
2 zeigt, dass innerhalb der Task-Funktion Asynchronität in Synchronisation „geschnitten“ wurde. Das Ganze ist nur eine Funktion, deren Ausführung etwas Zeit in Anspruch nimmt.
Aus formaler Sicht ist die Synthese von 1 und 2 „die Aufgabe als Ganzes eine asynchrone Funktion und der gesamte interne Teil ist synchron“, was als „äußerlich unterschiedlich, aber unterschiedlich“ bezeichnet wird das Gleiche auch im Inneren".
Die gesamte Funktion ist eine asynchrone Funktion, die nicht schwer zu verstehen ist. In Bezug auf die Implementierung können wir es genauso gut umkehren. Wenn das Schlüsselwort async aufgerufen wird, muss am Ende der Funktionsausführung ein Versprechen hinzugefügt werden:
async fn () { let result; // ... //末尾返回 promise return isPromise(result)? result : Promise.resolve(undefined); }
ist intern Wie wird die Synchronisation erreicht? Tatsächlich führt der Warteaufruf dazu, dass die folgenden Anweisungen (Funktionen) rekursiv ausgeführt werden. Sie werden erst dann aufgelöst, wenn das Ergebnis vorliegt und ihr Status geändert wird. Erst nachdem die Auflösung aufgelöst wurde, gilt die Wartecodezeile als abgeschlossen und wird mit der Ausführung zur nächsten Zeile fortfahren. Obwohl es außerhalb eine große for-Schleife gibt, wird daher die gesamte for-Schleife der Reihe nach serialisiert.
Allein aus dem Erscheinungsbild des obigen Frameworks ist es also nicht schwer, die Bedeutung von Async + Wait zu verstehen. Es ist einfach zu bedienen, aber Promise ist ein grundlegendes Stück, das man beherrschen muss.
秉承本次《重读 ES6》系列的原则,不过多追求理解细节和具体实现过程。我们继续巩固一下这个 “形式化” 的理解。
async + await 的进一步理解
有这样的一个异步操作 longTimeTask,已经用 Promise 进行了包装。借助该函数进行一系列验证。
const longTimeTask = function (time) { return new Promise((resolve, reject) => { setTimeout(()=>{ console.log(`等了 ${time||'xx'} 年,终于回信了`); resolve({'msg': 'task done'}); }, time||1000) }) }
async 函数的执行情况
如果,想查看 async exec1 函数的返回结果,以及 await 命令的执行结果:
const exec1 = async function () { let result = await longTimeTask(); console.log('result after long time ===>', result); } // 查看函数内部执行顺序 exec1(); // => 等了 xx 年,终于回信了 // => result after long time ===> Object {msg: "task done"} //查看函数总体返回值 console.log(exec1()); // => Promise {[[PromiseStatus]]: "pending",...} // => 同上
以上 2 步执行,清晰的证明了 exec1 函数体内是同步、逐行逐行执行的,即先执行完异步操作,然后进行 console.log() 打印。而 exec1() 的执行结果就直接是一个 Promise,因为它最先会蹦出来一串 Promise ...,然后才是 exec1 函数的内部执行日志。
因此,所有验证,完全符合 整体是一个异步函数,内部整个是同步的 的总结。
await 如何执行其后语句?
回到 await ,看看它是如何执行其后边的语句的。假设:让 longTimeTask() 后边直接带 then() 回调,分两种情况:
1)then() 中不再返回任何东西
2) then() 中继续手动返回另一个 promise
const exec2 = async function () { let result = await longTimeTask().then((res) => { console.log('then ===>', res.msg); res.msg = `${res.msg} then refrash message`; // 注释掉这条 return 或 手动返回一个 promise return Promise.resolve(res); }); console.log('result after await ===>', result.msg); } exec2(); // => 情况一 TypeError: Cannot read property 'msg' of undefined // => 情况二 正常
首先,longTimeTask() 加上再多得 then() 回调,也不过是放在了它的回调列队 queue 里了。也就是说,await 命令之后始终是一条 表达式语句,只不过上述代码书写方式比较让人迷惑。(比较好的实践建议是,将 longTimeTask 方法身后的 then() 移入 longTimeTask 函数体封装起来)
其次,手动返回另一个 promise 和什么也不返回,关系到 longTimeTask() 方法最终 resolve 出去的内容不一样。换句话说,await 命令会提取其后边的promise 的 resolve 结果,进而直接导致 result 的不同。
值得强调的是,await 命令只认 resolve 结果,对 reject 结果报错。不妨用以下的 return 语句替换上述 return 进行验证。
return Promise.reject(res);
最后
其实,关于异步编程还有很多可以梳理的,比如跨模块的异步编程、异步的单元测试、异步的错误处理以及什么是好的实践。All in all, 限于篇幅,不在此汇总了。最后,async + await 确实是一个很优雅的方案。
Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der async+await-Synchronisations-/asynchronen Lösung von ES6. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!