Nehmen wir an, es gibt eine Funktion auf Ihrer Website, die nur zu 5 % der Zeit verwendet wird. Diese Funktion benötigt etwas HTML, CSS und JavaScript, um zu funktionieren. Sie entscheiden sich also, anstatt dieses HTML, CSS und JavaScript direkt auf der Seite zu haben, es per Ajax einzuladen, wenn die Funktion bald verwendet wird.
Wir müssen drei Ajax-Anfragen stellen. Da wir dem Benutzer nichts anzeigen möchten, bis die Funktion einsatzbereit ist (und sie sich alle gegenseitig bedingen, damit sie richtig funktionieren), müssen wir warten, bis alle drei abgeschlossen sind, bevor wir fortfahren.
Was ist der beste Weg, das zu tun?
Ajax-Aufrufe in jQuery bieten Callbacks
$.ajax({
statusCode: {
url: "/feature",
success: function() {
// Ajax success
}
}
});
Oder auf die "Deferred"-Art, diesmal mit der Kurzform der $.get()-Methode
$.get("/feature/").done(function() {
// Ajax success
});
Aber wir müssen drei Ajax-Anfragen durchführen und wollen warten, bis alle drei abgeschlossen sind, bevor wir etwas tun. Das könnte im Callback-Dschungel ziemlich unübersichtlich werden.
// Get the HTML
$.get("/feature/", function(html) {
// Get the CSS
$.get("/assets/feature.css", function(css) {
// Get the JavaScript
$.getScript("/assets/feature.js", function() {
// All is ready now, so...
// Add CSS to page
$("<style />").html(css).appendTo("head");
// Add HTML to page
$("body").append(html);
});
});
});
Dies wartet erfolgreich, bis alles bereit ist, bevor etwas zur Seite hinzugefügt wird. So ist bis zu dem Zeitpunkt, an dem der Benutzer etwas sieht, alles in Ordnung. Das mag für manche Übelkeit hervorrufen, aber ich habe so etwas schon früher gemacht. Zumindest ergibt es Sinn und funktioniert. Das Problem? Es ist langsam.
Eine Anfrage ... warten, bis sie fertig ist ... eine weitere Anfrage ... warten, bis sie fertig ist ... eine weitere Anfrage ... warten, bis sie fertig ist ... fertig.
Es wäre schneller, wenn wir könnten
Alle drei Anfragen parallel ... warten, bis alle drei fertig sind ... fertig.
Hier können wir etwas Deferred / Promises-Magie nutzen. Ich bin sicher, das ist für einige von Ihnen JavaScript 101, aber diese Art von Dingen ist mir lange entgangen und komplexere Promise-Sachen immer noch.
In unserem einfachen Anwendungsfall können wir die $.when()-Methode von jQuery verwenden, die eine Liste dieser "Deferred"-Objekte (Alle jQuery Ajax-Methoden geben Deferred-Objekte zurück) entgegennimmt und dann einen einzelnen Callback bereitstellt.
$.when(
// Deferred object (probably Ajax request),
// Deferred object (probably Ajax request),
// Deferred object (probably Ajax request)
).then(function() {
// All have been resolved (or rejected), do your thing
});
Unser Callback-Höllen kann also umgeschrieben werden wie
$.when(
// Get the HTML
$.get("/feature/", function(html) {
globalStore.html = html;
}),
// Get the CSS
$.get("/assets/feature.css", function(css) {
globalStore.css = css;
}),
// Get the JS
$.getScript("/assets/feature.js")
).then(function() {
// All is ready now, so...
// Add CSS to page
$("<style />").html(globalStore.css).appendTo("head");
// Add HTML to page
$("body").append(globalStore.html);
});
Ein weiterer Anwendungsfall: Senf schneiden
Mein Anwendungsfallbeispiel ist eine 5%-Funktion. Halten Sie die Seite für die 95 % der Benutzer, die die Funktion nicht nutzen, leichter und machen Sie sie für diejenigen, die sie nutzen, zu einer relativ schnellen Ergänzung.
Eine andere Situation könnte eine "Senfschneide"-Situation sein, bei der Sie in bestimmten Situationen zusätzliche Funktionen oder Inhalte zu einer Seite hinzufügen, je nachdem, was Sie entscheiden. Vielleicht führen Sie einen matchMedia-Test mit einigen Media Queries durch und stellen fest, dass Bildschirmgröße und Fähigkeiten des Geräts so sind, dass Sie einige zusätzliche Module einbinden möchten. Toll, machen Sie das mit einigen parallelen Ajax-Aufrufen!
Das war die einfachste Erklärung für jQuery Deferred, die ich gefunden habe. Sehr gut! Danke Chris!
Hallo Chris, danke für den Artikel.
Gibt es einen Grund, warum Sie die abgerufenen Ergebnisse lieber im globalen Scope speichern?
Funktioniert auch gut. :)
Das ist wahrscheinlich besser und vielleicht sogar angenehmer für Leute, die konvertieren. Ich habe so etwas versucht und es hat nicht ganz funktioniert, aber ich bin sicher, ich habe es einfach vermasselt.
Die
then-Callback-Funktion nimmt also die Ergebnisse der Deferred-Objekte als Argumente entgegen. Interessant und hilfreich. Danke für den Tipp!Ich habe danach eine Weile gesucht, aber ohne Erfolg. Alle Erklärungen waren so schwierig. Danke!
Schöner Artikel! Ich mag es wirklich, Promises zu verwenden, um Callback-Hölle zu vermeiden.
Aber warum das "globalstore"-Objekt?
Sie könnten einfach
Beispiel: http://jsfiddle.net/C4vym/
Dies ist für viele Programmierer vielleicht auch alter Hut, aber ich habe diesen Artikel darüber gefunden, wie sich der asynchrone Ansatz vom Multithreading-Ansatz unterscheidet. Die Analogie mit einem Burgerladen, wo Verkäufer den Kunden Burger servieren, ist nett, wobei der Grill und der Ofen Ressourcen darstellen. Ich frage mich, ob sie wirklich zutreffend ist?
http://alookonthecode.blogspot.com/2012/10/simply-explained-multithreaded-vs-async.html
Danke für dieses Tutorial, es ist wirklich sehr nützlich. Ich habe kürzlich an einem Projekt gearbeitet, bei dem es darum ging, Vorlagen und JSONs mit Daten zu laden, um sie auszufüllen. Da ich diese Technik nicht kannte, war alles so: Vorlage laden – warten – JSON laden – warten – mit den Daten arbeiten – anzeigen. Das hilft wirklich.
In meinem Projekt weiß ich nicht, ob die Quelle, die ich mit Ajax abrufen möchte, existiert oder nicht. Ich kann mich nicht auf den "then"-Callback verlassen. Er schlägt fehl, wenn die Quelle nicht heruntergeladen werden kann. Wenn Sie sich fragen, wie Sie mit dieser Situation umgehen sollen, ist dies der richtige Weg
Sie können dafür einfach .then() verwenden
http://api.jquery.com/deferred.then/
Wenn Sie diese Art von Dingen mögen (und ich mag sie sehr), dann wird das hier Ihren Verstand sprengen
Sie können auch das Laden von mehreren Bildern über Promises anstellen, zum Beispiel
Etwas über die Bedürfnisse vieler hinaus, aber das ist verdammt genial.
Ich habe gestern nach der jQuery "when"-Funktion gesucht und jetzt sehe ich ein echtes Tutorial dazu. Das ist eine tolle Funktion! Ich habe vorher die Anfrage-in-Anfrage-Technik verwendet und mich sehr über die Geschwindigkeit geärgert. Das ist sehr nützlich, wenn wir eine App entwickeln, die zwischen Client und Server kommuniziert.
Danke für das Teilen.
Würde das auch funktionieren? Alle AJAX-Callbacks rufen dieselbe Funktion auf, aber diese Funktion wird erst ausgeführt, wenn alle drei zurückgekommen sind. Das bedeutet, die AJAX-Aufrufe müssen nicht an derselben Stelle im Code stehen und können einzeln wiederverwendet werden.
Das ist nicht falsch, aber es fühlt sich für mich etwas eigenartig an. Wie eine absichtliche Vermeidung von Struktur. Aber es wäre trotzdem schneller als verschachtelte Callbacks.
David, genau! Ihr Schnipsel funktioniert auf die gleiche Weise. Bitte schauen Sie sich den Kommentar von nekman oben an. Ich denke, Promises sind viel sauberer und haben mehr Möglichkeiten zur Erweiterung.
David, Ihr Code funktioniert, ist aber unnötig, wenn Sie an Wiederverwendbarkeit denken. Wenn Sie diese AJAX-Aufrufe in Zukunft wiederverwenden möchten, packen Sie sie in eine Funktion und geben Sie für jeden AJAX-Aufruf das Promise-Objekt zurück. Sie erhalten alle Vorteile von Promises und können die AJAX-Aufrufe bei Bedarf separat aufrufen.
Danke für das Feedback, Leute. Ja, Benjamin, ich mag Ihre Methode, sie ist weniger hacky als meine.
Das ist ungefähr das, was jQuery intern macht. Wann immer ein Promise bereit ist, ruft es eine Funktion auf, bis alle Promises abgeschlossen sind.
Ihr Code ist weder eigenartig noch schlecht. Es ist ein Äquivalent in reinem/nativem JavaScript, das viele unnötige jQuery-Aufrufe vermeidet, also verwenden Sie, was Sie bevorzugen.
Kürzlich habe ich die "Mobile First"-Ansatz-Demo erstellt. Mein Beispiel zeigt, wie man an matchMedia bindet und Assets bei Bedarf lädt. Die interne Logik ist sehr einfach: Verwenden Sie
<link>- und<script>-Tags, um Assets für Mobilgeräte zu laden; binden Sie an bestimmte Media-Queries, um zusätzliche Assets zu laden, wenn ein bestimmtes Ereignis eintritt. Es funktioniert also für mich einwandfrei.Ich benutze das bekannte yepnope.js, um CSS und JS dort zu laden. Es bietet eine gute Abstraktionsebene und erspart auch die Callback-Hölle.
Wirklich schöne Informationen. Es reduziert die Ladezeit und sorgt für eine gute Benutzererfahrung.
Früher habe ich alle meine DOM-Manipulationen mit Callbacks wie diesen in jQuery gemacht. Aber je größer und größer das Projekt wurde, desto komplexer wurde alles, dass es einen verrückt machen kann. Heutzutage verwende ich Reactjs, um verschiedene Abschnitte meiner Seite zu rendern, und aus Designerperspektive ist Reactjs im Vergleich zu anderen komplexen Lösungen wie Angularjs oder Emberjs sehr cool.
Interessant, aber dieser Teil ist meiner Meinung nach ziemlich knifflig
Ich stimme hier nicht zu, es ist eher wie
Anfrage #1 senden, Anfrage #2 senden, Anfrage #3 senden.
Ergebnis #1 verarbeiten, Ergebnis #2 verarbeiten, Ergebnis #3 verarbeiten.
Da diese Funktionsaufrufe asynchron sind, werden die Anfragen im ursprünglich geschriebenen Code bereits asynchron gesendet.
Am Ende denke ich also, dass es ziemlich dasselbe ist.
Ach, ich habe mich beim Code von David Gilbertson geirrt (https://css-tricks.de/multiple-simultaneous-ajax-requests-one-callback-jquery/#comment-1155012)
Danke für diesen Trick... Werde das beim nächsten Mal ausprobieren.
Ihr anfänglicher Codeblock hat eine überflüssige
statusCode-Schicht. Die korrekte Version istIch habe fast die gleiche Technik hier verwendet: http://horiajurcut.com/the-perfect-request/
Das ist eine sehr nützliche Technik, um bedingt zusätzlichen Inhalt schnell zu laden. Danke. :)
Wirklich nützlich... danke! :)
Schöner Snippet. Danke.
Wenn ich mir die Kommentare ansehe, kann jemand bitte Promises erläutern?
Nichts Neues, aber die Erklärung war geradlinig und sehr einfach zu befolgen. Beste Einführung in Deferreds.
Sie können auch einen Callback für jQuery html() verwenden
Was ist die maximale Anzahl an Ajax-Aufrufen, die es auf einmal machen kann?
Die Serverlast und die Anzahl der Client-Anfragen an den Server sollten gleich sein. Ich habe diesen Trick noch nicht getestet, aber anfangs denke ich, dass es keine Unterschiede zwischen diesen Methoden und der normalen Methode geben würde.
Gute Erklärung und einige sehr hilfreiche Tipps. Vielen Dank.
Hier ist das Muster, das ich normalerweise verwende
Ich habe ein paar Beiträge darüber veröffentlicht, wie man $.when mit Backbone verwendet
Warten auf das Laden mehrerer Sammlungen &
Anwendungsbootstrapping
Löschen Sie das. Ich wollte die ersten paar Zeilen bereinigen, die ich von oben kopiert habe
Schauen Sie sich Q und BlueBird an, wenn Sie Promises verwenden, sie sind die schnellsten und ergeben für mich am meisten Sinn. jQuerys Deferred-Methode erfüllt nicht vollständig die Promises-Spezifikation und ist neben Vanilla JavaScript-Implementierungen eine der langsamsten. http://jsperf.com/promise-comparisons/63
Der Code-Schnipsel nach diesem Text: "In unserem einfachen Anwendungsfall können wir die $.when()-Methode von jQuery verwenden, die eine Liste dieser „Deferred“-Objekte (Alle jQuery Ajax-Methoden geben Deferred-Objekte zurück) entgegennimmt und dann einen einzelnen Callback bereitstellt." hat ein Problem. Sie haben $.when( … }.then
Danke! Behoben. Aus dem Thread verschoben, da nicht mehr relevant.
matchMedia. Danke, dass Sie das erwähnt haben. Sieht so aus, als hätten Irish und Kollegen auch ein Polyfill dafür.
Zeitsparendste und klarste Erklärung und Beispiele. Vielen Dank!
Vielen Dank für diese tolle Erklärung zur Verwendung von jQuery.when().
Tusko Trush hat diese Idee eingebracht