Ich möchte ein theoretisches Problem darlegen.
Angenommen, ich habe eine unendliche Schriftrolle, die wie hier beschrieben implementiert ist: https://medium.com/frontend-journeys/how-virtual-infinite-scrolling-works-239f7ee5aa58 . Es ist nichts Besonderes daran zu genügen, zu sagen, dass es sich um eine Datentabelle handelt, z. B. NxN, und der Benutzer kann wie eine Tabelle nach unten und rechts scrollen, und es werden nur die Daten in der aktuellen Ansicht plus minus a angezeigt Griff.
Nehmen wir nun an, dass das Abrufen und Anzeigen der Daten in dieser Ansicht ungefähr 10 ms dauert, mit einer Funktion wie:
get_data(start_col, end_col, start_row, end_row);
Dies wird sofort geladen, wenn Sie auf eine beliebige Stelle in der Bildlaufleiste klicken oder einen leichten Bildlauf durchführen, um die erforderlichen Daten zu rendern. Nehmen wir jedoch auch an, dass für jedes 'unvollendete Abrufereignis' die doppelte Zeit zum Rendern der erforderlichen Ansichtsdaten benötigt wird (aufgrund von Speicher, gc und einigen anderen Dingen). Wenn ich also langsam und absichtlich von links nach rechts scrolle, kann es sein, dass ich mehr als 100 Bildlaufereignisse generiere, die das Laden von Daten auslösen - zunächst gibt es keine spürbare Verzögerung von Null. Der Abruf erfolgt in weniger als 10 ms, aber bald dauert es 20 ms und dann 40 ms, und jetzt haben wir so etwas wie eine merkliche Verzögerung, bis er über eine Sekunde reicht, um die erforderlichen Daten zu laden. Außerdem können wir so etwas wie eine Entprellung / Verzögerung nicht verwenden.
Welche Überlegungen müsste ich berücksichtigen und wie würde ein Beispielalgorithmus aussehen, um dies zu erreichen? Hier ist ein Beispiel für die Benutzerinteraktion, die ich für die Daten haben möchte, unter der Annahme einer Tabelle mit 10000 x 10000 (obwohl Excel alle Daten gleichzeitig laden kann) - https://gyazo.com/0772f941f43f9d14f884b7afeac9f414 .
quelle
Antworten:
Ich denke, Sie sollten bei keinem Scroll-Event eine Anfrage senden. Nur wenn der Benutzer mit dieser Schriftrolle das Ende der Schriftrolle erreicht.
Sie können auch eine Breite angeben, die als nah genug definiert wird, um neue Daten abzurufen (z
e.target.scrollHeight - e.target.offsetHeight <= 150
).quelle
Manchmal ist der beste Ansatz ein Prototyp, und als ich das Problem interessant fand, verbrachte ich ein wenig Zeit damit, einen zu kochen, obwohl er als Prototyp zugegebenermaßen viele Warzen hat ...
Kurz gesagt, die einfachste Lösung zur Begrenzung eines Rückstands bei Datenabrufen scheint darin zu bestehen, einfach den Mutex eines armen Mannes innerhalb der Routine einzurichten , die das Abrufen durchführt. (Im folgenden Codebeispiel lautet die simulierte Abruffunktion
simulateFetchOfData
.) Beim Mutex wird eine Variable außerhalb des Funktionsumfangs so eingerichtet, dassfalse
der Abruf zur Verwendung geöffnet ist undtrue
der Abruf gerade ausgeführt wird.Das heißt, wenn der Benutzer den horizontalen oder vertikalen Schieberegler anpasst, um einen Datenabruf zu initiieren, prüft die Funktion, die die Daten abruft, zuerst, ob die globale Variable
mutex
wahr ist (dh ein Abruf ist bereits im Gange), und wird in diesem Fall einfach beendet . Wenn diesmutex
nicht der Fall ist, wird der Wertmutex
auf true gesetzt und der Abruf fortgesetzt. Und natürlich am Ende der Abruffunktion,mutex
auf false gesetzt, so dass das nächste Benutzereingabeereignis dann die Mutexprüfung vorne durchläuft und einen weiteren Abruf durchführt ...Ein paar Anmerkungen zum Prototyp.
simulateFetchOfData
Funktion ist der Schlaf (100) als Versprechen konfiguriert, das die Verzögerung beim Abrufen der Daten simuliert. Dies ist mit etwas Protokollierung an der Konsole eingeklemmt. Wenn Sie die Mutex-Prüfung entfernen, werden Sie bei geöffneter Konsole feststellen, dass beim Verschieben der Schieberegler viele Instanzen vonsimulateFetchOfData
initiiert und in Spannung gesetzt werden, während der Mutex-Prüfung auf den Ruhezustand (dh den simulierten Datenabruf) wartet An Ort und Stelle wird jeweils nur eine Instanz initiiert.mutex
auf false eine Überprüfung durchgeführt wird, um festzustellen, ob die horizontalen und vertikalen Bildlaufwerte ausgerichtet sind. Wenn nicht, wird ein weiterer Abruf eingeleitet. Dies stellt sicher, dass trotz einer Anzahl von Bildlaufereignissen, die möglicherweise nicht ausgelöst werden, weil der Abruf beschäftigt ist, mindestens die endgültigen Bildlaufwerte durch Auslösen eines endgültigen Abrufs adressiert werden.buffer
wird verwendet, um die "abgerufenen" Daten zu speichern. Wenn Sie es in der Konsole untersuchen, werden viele "leere x XXXX" -Einträge angezeigt. DiesimulateFetchOfData
Funktion ist so eingerichtet, dass, wenn die Daten bereits gespeichert sindbuffer
, kein "Abrufen" durchgeführt wird.(Um den Prototyp anzuzeigen, kopieren Sie einfach den gesamten Code und fügen Sie ihn in eine neue Textdatei ein, benennen Sie ihn in ".html" um und öffnen Sie ihn in einem Browser. BEARBEITEN: Wurde auf Chrome und Edge getestet.)
Auch dies ist ein Prototyp, um ein Mittel zur Begrenzung eines Rückstands unnötiger Datenanrufe zu beweisen. Wenn dies für Produktionszwecke umgestaltet werden soll, müssen viele Bereiche adressiert werden, einschließlich: 1) Reduzierung der Nutzung des globalen variablen Raums; 2) Hinzufügen von Zeilen- und Spaltenbeschriftungen; 3) Hinzufügen von Schaltflächen zu den Schiebereglern zum Scrollen einzelner Zeilen oder Spalten; 4) möglicherweise verwandte Daten puffern, wenn Datenberechnungen erforderlich sind; 5) etc ...
quelle
Es gibt einige Dinge, die getan werden könnten. Ich sehe es als eine zweistufige Zwischenschicht zwischen der Datenanforderungsprozedur und dem Benutzer-Scroll-Ereignis.
1. Verzögern Sie die Verarbeitung von Bildlaufereignissen
Sie haben Recht, Debounce ist nicht unser Freund in den Scroll-bezogenen Fragen. Es gibt jedoch den richtigen Weg, um die Anzahl der Schüsse zu verringern.
Verwenden Sie die gedrosselte Version des Scroll-Ereignishandlers, die höchstens einmal pro festem Intervall aufgerufen wird. Sie können Lodash-Gas verwenden oder eine eigene Version [ 1 ], [ 2 ], [ 3 ] implementieren . Stellen Sie 40 - 100 ms als Intervallwert ein. Sie müssen auch die
trailing
Option festlegen, damit das allerletzte Bildlaufereignis unabhängig vom Timerintervall verarbeitet wird.2. Intelligenter Datenfluss
Wenn der Scroll-Ereignishandler aufgerufen wird, sollte der Datenanforderungsprozess initiiert werden. Wie Sie bereits erwähnt haben, kann es bei jedem Scroll-Ereignis (auch wenn wir mit dem Drosseln fertig sind) zu Zeitverzögerungen kommen. Möglicherweise gibt es einige gängige Strategien: 1) Fordern Sie die Daten nicht an, wenn eine andere ausstehende Anforderung vorliegt. 2) die Daten nicht mehr als einmal pro Intervall anfordern; 3) vorherige ausstehende Anfrage abbrechen.
Der erste und der zweite Ansatz sind nicht mehr als das Entprellen und Drosseln auf Datenflussebene. Das Entprellen kann mit minimalem Aufwand mit nur einer Bedingung implementiert werden, bevor die Anforderung + eine zusätzliche Anforderung am Ende initiiert wird. Aber ich glaube, dass Gas aus UX-Sicht angemessener ist. Hier müssen Sie eine Logik bereitstellen und die
trailing
Option nicht vergessen, wie sie im Spiel sein sollte.Der letzte Ansatz (die Stornierung von Anforderungen) ist ebenfalls UX-freundlich, aber weniger vorsichtig als der drosselnde. Sie starten die Anforderung trotzdem, werfen aber das Ergebnis weg, wenn nach dieser Anforderung eine andere Anforderung gestartet wurde. Sie können auch versuchen, die Anforderung abzubrechen, wenn Sie verwenden
fetch
.Meiner Meinung nach besteht die beste Option darin, die Strategien (2) und (3) zu kombinieren. Sie fordern die Daten also nur an, wenn seit dem Initiieren der vorherigen Anfrage ein festes Zeitintervall verstrichen ist, UND Sie brechen die Anfrage ab, wenn danach eine andere initiiert wurde .
quelle
Es gibt keinen spezifischen Algorithmus, der diese Frage beantwortet, aber um keine Verzögerung aufzubauen, müssen Sie zwei Dinge sicherstellen:
1. Keine Speicherlecks
Stellen Sie unbedingt sicher, dass nichts in Ihrer App neue Instanzen von Objekten, Klassen, Arrays usw. erstellt. Der Speicher sollte nach 10 Sekunden Scrollen derselbe sein wie nach 60 Sekunden usw. Sie können Datenstrukturen vorab zuweisen, wenn Sie müssen (einschließlich Arrays) und sie dann wiederverwenden:
2. Ständige Wiederverwendung von Datenstrukturen
Dies ist bei unendlichen Bildlaufseiten üblich. In einer unendlichen Bildlaufgalerie, in der höchstens 30 Bilder gleichzeitig auf dem Bildschirm angezeigt werden, werden möglicherweise nur 30-40
<img>
Elemente erstellt. Diese werden dann beim Scrollen des Benutzers verwendet und wiederverwendet, sodass keine neuen HTML-Elemente erstellt (oder zerstört und daher mit Müll gesammelt werden müssen). Stattdessen erhalten diese Bilder neue Quell-URLs und neue Positionen, und der Benutzer kann weiter scrollen, aber (ohne dass sie es wissen) sehen sie immer wieder dieselben DOM-Elemente.Wenn Sie Canvas verwenden, werden Sie keine DOM-Elemente verwenden, um diese Daten anzuzeigen, aber die Theorie ist dieselbe, nur die Datenstrukturen sind Ihre eigenen.
quelle