Browserbasierter Ereignisabfragemechanismus (und der Ereignisabfragemechanismus in Node.js), JavaScript wird häufig ausgeführt eine asynchrone Umgebung. Aufgrund der Eigenschaften der JavaScript-eigenen Sprache (es ist nicht erforderlich, dass Programmierer Threads/Prozesse steuern) ist es sehr wichtig, die asynchrone Programmierung in js zu lösen. Man kann sagen, dass es für js-Entwickler in einem vollständigen Projekt unmöglich ist, nicht mit asynchronen Vorgängen konfrontiert zu werden. In diesem Artikel werden mehrere klassische Serialisierungsmethoden für die asynchrone JavaScript-Programmierung ausführlich vorgestellt. Außerdem wird kurz die von ES6 bereitgestellte sequentielle Ausführungsmethode Promise vorgestellt.
Angenommen, wir haben eine ajax()
-Methode, die einen URL-Parameter empfängt, Initiate an asynchrone Anfrage an die Adresse und führen Sie den zweiten Parameter aus – eine Rückruffunktion am Ende der Anfrage:
ajax(url,function(result){ console.log(result); });
Man kann sagen, dass diese Methode die Rückruffunktionsmethode ist, die von fast jedem Front-End verwendet wird Entwickler: Mit einem solchen Rückrufmechanismus müssen Entwickler keinen Code wie den folgenden schreiben, um zu spekulieren, wann die Serveranforderung zurückgegeben wird:
var result=ajax(url); setTimeout(function(result){ console.log(result); },400);
Sie sollten in der Lage sein, zu verstehen, was ich hier ausdrücken möchte. Wir stellen einen Timer mit einer Verzögerung von 400 Millisekunden ein und gehen davon aus, dass die von uns gestellte Ajax-Anfrage innerhalb von 400 Millisekunden abgeschlossen wird. Andernfalls operieren wir mit einem undefined
von result
.
Aber es gibt ein Problem, das mit der Erweiterung des Projekts allmählich auftaucht: Wenn die Szene mehrere Ebenen verschachtelter Rückruffunktionen erfordert, wird der Code schwer zu lesen und zu warten:
ajax(url0,function(result0){ ajax(result0.url1,function(result1){ ajax(result1.url2,function(result2){ console.log(result2); }); }); });
Um das Code-Verwirrungsproblem zu lösen, das durch Inline-Callback-Funktionen aufgedeckt wird, führen wir externe Funktionsaufrufe ein, um ähnliche Probleme zu lösen:
function handle2(result){ console.log(result); }function handle1(result){ ajax(result.url,function(result){ handle2(result); }); } ajax(url,function(result){ handle1(result); });
Durch diese Aufteilung der Inline-Funktionen , nennen wir Die Optimierungsmethode externer Funktionen kann die Einfachheit des Codes erheblich aufrechterhalten.
Durch die Beobachtung beliebter JavaScript-Prozesssteuerungstools wie Nimble, Step und Seq lernen wir ein einfaches Entwurfsmuster: Steuerung durch einen Callback-Manager. Asynchrone JavaScript-Ausführung Flow, das Folgende ist ein Schlüsselcodebeispiel eines typischen Callback-Managers:
var Flow={};//设置next方法,在上一个方法完成时调用下一个方法Flow.next=function(){ if(this.stack[0]){ //弹出方法栈中的第一个方法,并执行他 this.stack.shift()(); } };//设置series方法,接收一个函数数组,并按序执行Flow.series=function(arr){ this.stack=arr; this.next(); };//通过Flow.series我们能够控制传入的函数的执行顺序Flow.series([ function(){ //do something console.log(1); Flow.next(); }, function(next){ //do something console.log(2); Flow.next(); } ]);
Wir haben einen Flow
-Controller initialisiert und zwei series
- und next
-Funktionsattribute dafür entworfen. Innerhalb der von uns geschriebenen Geschäftsmethode wird die nächste Methode nacheinander durch Aufrufen von Flow.next()
am Ende der Methode ausgelöst. Die asynchrone Funktion wird nacheinander durch Ausführen der Methode series
ausgeführt. Diese Art der Verwaltung asynchroner Funktionsaufrufe über den Kerncontroller vereinfacht unseren Programmierprozess und ermöglicht es Entwicklern, mehr Energie in die Geschäftslogik zu investieren.
Möglicherweise kann die oben eingeführte asynchrone Methode die Geschäftsszenarien in der tatsächlichen Entwicklung immer noch nicht erfüllen: Angenommen, wir haben a()
, b()
, c()
drei Methoden, a und b, haben keine Abhängigkeit und können asynchron ausgeführt werden. Aber c kann erst ausgelöst werden, nachdem sowohl a als auch b abgeschlossen sind. Um eine solche logische Implementierung zu erfüllen, fügen wir einen globalen Zähler hinzu, um den Ausführungsfluss des Codes zu steuern:
var flag=2;var aValue,bValue;function a(){ aValue=1; flag--; c(); }function b(){ setTimeout(function(){ bValue=2; flag--; c(); },200); }function c(){ if(flag==0){ console.log("after a and b:"+(aValue+bValue)); } } a(); b();
Wir setzen ein globales Variablenflag, um den Abschluss von Methode a und Methode b zu überwachen. Methode b simuliert die Netzwerkumgebung, indem sie einen 200-Millisekunden-Timer einstellt, und ruft schließlich Methode c erfolgreich auf, nachdem Methode b ausgeführt wurde. Auf diese Weise implementieren wir abhängige Aufrufe der Methoden a()
, b()
und c()
.
Wenn die obige Lösung in komplexen Szenarien angewendet wird, treten die folgenden Probleme auf: Das Produkt hat mehrere Versionsiterationen durchlaufen, und die C-Methode ist darauf angewiesen mehr Methoden, daher muss das Zählerflag ständig geändert werden; Entwickler werden während des Produktiterationsprozesses geändert. Wenn die beiden oben genannten Situationen auftreten, wird die Logik des Codes verwirrend. Ob das Flag-Tag prägnant und korrekt bleiben kann, wird weitgehend von Produktiterationen beeinflusst. Daher schlagen wir datenorientierte Optimierungsverbesserungen vor.
In realen Entwicklungsszenarien liegt der Grund für die Existenz von Methodenabhängigkeiten im Wesentlichen in der Existenz von Datenabhängigkeiten. Für das obige einfache Beispiel: Methode c hängt von den Ergebnissen der Operationen von Methode a und Methode b ab und nicht ob das Flag 0 ist. Daher können wir die Prüfung, ob der Marker auf 0 gesetzt wurde, durch die Prüfung ersetzen, ob die abhängige Methode die Datenverarbeitung abgeschlossen hat. In diesem Beispiel prüfen wir, ob aValue und bValue die Zuweisung in der c-Methode abgeschlossen haben:
function c(){ if(aValue!==undefined && bValue!==undefined){ console.log("after a and b:"+(aValue+bValue)); } }
Für ein allgemeineres Szenario ändern wir den obigen Code wie folgt:
var checkDependency={};var aValue,bValue;function a(){ aValue=1; checkDependency.a=true; c(); }function b(){ setTimeout(function(){ bValue=2; checkDependency.b=true; c(); },200); }function c(){ if(checkDependency.a && checkDependency.b){ console.log("after a and b:"+(aValue+bValue)); } } a(); b();
Bei der datenorientierten Inspektionsmethode müssen wir bei einer zukünftigen Erweiterung nur Paare zur neuen Methode hinzufügencheckDependency
Ändern Sie das Objekt und überprüfen Sie das Vorhandensein der entsprechenden Attribute in der C-Methode, um die sequentielle Ausführung asynchroner abhängiger Methoden zu erreichen.
Um die Komplexität asynchroner Methoden in JavaScript zu lösen, führte der Beamte eine einheitliche Kontrollmethode ein:
var bool=false;/* * 新建一个Promise实例,向构造函数传入一个异步执行函数 * 异步函数会接受两个参数,由Promise传入,对应then方法中传入的方法 */var promise=new Promise(function(resolve,reject){ setTimeout(function(){ if(bool){ //根据执行情况相应调用resolve和reject resolve(bool); }else{ reject(bool); } },200); });//通过then向Promise实例传入解决方法promise.then(function resolve(result){ console.log("success"); },function reject(result){ console.log("failure"); });
Die Der obige Beispielcode zeigt eine grundlegende Promise-Anwendung. Möglicherweise kommt der folgende Kettenaufruf in tatsächlichen Szenarien häufiger vor:
new Promise(function(res,rej){ if(/*异步调用成功*/){ res(data); }else{ rej(error); } }).then(function resolve(result){ console.log("success"); },function reject(result){ console.log("failure"); });
如果对Promise感兴趣的话,可以在网上寻找资料继续深入学习!
关于Promise的兼容性,通常web前端JavaScript代码中不会直接使用Promise(通过caniuse.com网站查询发现Android4.4不支持Promise)。如果特别想使用的,往往会在项目中附带一些补足兼容性的promise类库;而后端Node.js可以放心使用Promise类来管理异步逻辑。
以上就是详解JavaScript异步编程技术的内容,更多相关内容请关注PHP中文网(m.sbmmt.com)!