Bei der Arbeit an einem Projekt bin ich auf ein Problem der Blockierung von UI-Threads gestoßen, das durch synchronisiertes Ajax verursacht wurde. Lassen Sie mich meinen Problemlösungsprozess mit Ihnen teilen.
Der Grund dafür ist, dass es auf der Seite mehrere ähnliche asynchrone Anforderungsaktionen gibt. Um die Wiederverwendbarkeit des Codes zu verbessern, habe ich eine Funktion namens getData gekapselt, die verschiedene Parameter empfängt und nur für das Abrufen von Daten verantwortlich ist. und dann die Daten zurückgeben. Die grundlegende Logik lautet wie folgt:
function getData1(){ var result; $.ajax({ url : "p.php", async : false, success: function(data){ result = data; } }); return result; }
Der Ajax hier darf nicht asynchron sein, da sonst bei der Rückkehr der Funktion dem Ergebnis kein Wert zugewiesen wurde und ein Fehler auftritt. Also habe ich async:false hinzugefügt. Es scheint kein Problem zu geben. Ich kann die Daten normal abrufen, indem ich diese Funktion aufrufe.
$(".btn1").click(function(){ var data = getData1(); alert(data); });
Als nächstes möchte ich eine weitere Funktion hinzufügen, bevor ich die Anfrage sende, d werde es auch gesehen haben. Meine Verarbeitungsfunktion sieht also so aus:
$(".btn1").click(function(){ $(".loadingicon").show(); var data = getData1(); $(".loadingicon").hide(); alert(data); });
Zeigen Sie das Ladebild vor der Anfrage an und blenden Sie es aus, nachdem die Anfrage abgeschlossen ist. Es scheint kein Problem zu geben. Um den Effekt deutlich zu sehen, schläft mein p.php-Code 3 Sekunden lang wie folgt:
<?php sleep(3); echo ("aaaaaa"); ?>
Als ich es ausführte, trat jedoch ein Problem auf. Das Ladebild wurde nicht wie erwartet angezeigt und die Seite reagierte überhaupt nicht. Nach langer Fehlerbehebung habe ich den Grund gefunden, der async:false ist.
Der Rendering-Thread (UI) des Browsers und der JS-Thread schließen sich gegenseitig aus. Wenn zeitaufwändige JS-Vorgänge ausgeführt werden, wird das Rendern von Seiten blockiert. Es gibt kein Problem, wenn wir asynchrones Ajax ausführen, aber wenn es auf eine synchrone Anforderung eingestellt ist, werden andere Aktionen (der Code hinter der Ajax-Funktion und der Rendering-Thread) gestoppt. Auch wenn meine DOM-Operationsanweisung der Satz ist, bevor die Anfrage initiiert wird, wird diese Synchronisationsanforderung den UI-Thread „schnell“ blockieren, ohne ihm Zeit zur Ausführung zu geben. Aus diesem Grund schlägt der Code fehl.
setTimeout löst Blockierungsproblem
Nachdem wir nun verstanden haben, wo das Problem liegt, wollen wir eine Lösung finden. Um zu verhindern, dass die synchrone Ajax-Anfrage den Thread blockiert, dachte ich an setTimeout, füge den Anforderungscode in sestTimeout ein und lasse den Browser einen Thread neu starten, um zu funktionieren. Wäre das Problem nicht gelöst? Seitdem sieht mein Code so aus:
$(".btn2").click(function(){ $(".loadingicon").show(); setTimeout(function(){ $.ajax({ url : "p.php", async : false, success: function(data){ $(".loadingicon").hide(); alert(data); } }); }, 0); });
Der zweite Parameter von setTimeout ist auf 0 gesetzt und der Browser führt ihn nach einer festgelegten Mindestzeit aus. Egal was passiert, lassen Sie es uns zuerst ausführen und sehen.
Das Ergebnis-Ladebild wird angezeigt, aber! ! ! Warum bewegt sich das Bild nicht? Es ist offensichtlich ein animiertes GIF. Zu diesem Zeitpunkt dachte ich schnell, dass die Synchronisierungsanforderung zwar verzögert war, den UI-Thread jedoch während seiner Ausführung blockieren würde. Diese Blockierung ist so großartig, dass sich selbst das GIF-Bild nicht bewegt und wie ein statisches Bild aussieht.
Die Schlussfolgerung liegt auf der Hand. SetTimeout behandelt die Symptome, aber nicht die Grundursache. Dies ist gleichbedeutend damit, dass die Synchronisierungsanforderung „leicht“ asynchron ist und den Thread blockiert. Der Plan scheiterte.
Es ist Zeit, Deferred zu verwenden
jQuery hat nach Version 1.5 das Deferred-Objekt eingeführt, das einen sehr praktischen verallgemeinerten asynchronen Mechanismus bietet. Einzelheiten finden Sie in diesem Artikel von Lehrer Ruan Yifeng http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html.
Also habe ich den Code mit verzögerten Objekten wie folgt umgeschrieben:
function getData3(){ var defer = $.Deferred(); $.ajax({ url : "p.php", //async : false, success: function(data){ defer.resolve(data) } }); return defer.promise(); } $(".btn3").click(function(){ $(".loadingicon").show(); $.when(getData3()).done(function(data){ $(".loadingicon").hide(); alert(data); }); });
Sie können sehen, dass ich async:false in der Ajax-Anfrage entfernt habe, was bedeutet, dass die Anfrage wieder asynchron ist. Bitte beachten Sie auch diesen Satz in der Erfolgsfunktion: defer.resolve(data) Die Auflösungsmethode des Deferred-Objekts kann einen Parameter eines beliebigen Typs übergeben. Dieser Parameter kann in der Methode done abgerufen werden, sodass die von uns asynchron angeforderten Daten auf diese Weise zurückgegeben werden können.
Zu diesem Zeitpunkt ist das Problem gelöst. Aufgeschobene Objekte sind so leistungsstark und praktisch, dass wir sie nutzen können.
Alle meine Testcodes lauten wie folgt. Interessierte Schüler können sie zum Testen verwenden:
<button class="btn1">async:false</button> <button class="btn2">setTimeout</button> <button class="btn3">deferred</button> <img class="loadingicon" style="position:fixed;left:50%;top:50%;margin-left:-16px;margin-top:-16px;display:none;" src=http://www.update8.com/Web/Jquery/"loading2.gif" alt="正在加载" /> <script> function getData1(){ var result; $.ajax({ url : "p.php", async : false, success: function(data){ result = data; } }); return result; } $(".btn1").click(function(){ $(".loadingicon").show(); var data = getData1(); $(".loadingicon").hide(); alert(data); }); $(".btn2").click(function(){ $(".loadingicon").show(); setTimeout(function(){ $.ajax({ url : "p.php", async : false, success: function(data){ $(".loadingicon").hide(); alert(data); } }); }, 0); }); function getData3(){ var defer = $.Deferred(); $.ajax({ url : "p.php", //async : false, success: function(data){ defer.resolve(data) } }); return defer.promise(); } $(".btn3").click(function(){ $(".loadingicon").show(); $.when(getData3()).done(function(data){ $(".loadingicon").hide(); alert(data); }); });</script>
ps:Parameterbeschreibung von $.ajax
Parameterbeschreibung
URL erforderlich. Gibt die URL an, an die die Anfrage gesendet werden soll.
Daten sind optional. Karten- oder Zeichenfolgenwert. Gibt die Daten an, die mit der Anfrage an den Server gesendet werden sollen.
success(data, textStatus, jqXHR) Optional. Die Rückruffunktion wird ausgeführt, wenn die Anfrage erfolgreich ist.
dataType
Optional. Gibt den Datentyp der erwarteten Serverantwort an.
Die intelligente Beurteilung erfolgt standardmäßig (XML, JSON, Skript oder HTML).