Warum macht setTimeout () meine App verzögert, Rxjs timer (). Subscribe (…) jedoch nicht?

9

Ich habe eine Komponente, die einige Kommentare im Abstand von 100 ms "faul lädt".

Wenn ich setTimeout benutze, ist es wirklich verzögert.

Komponente

<div *ngFor="let post of posts">
   <app-post [post]="post" ></app-post>
</div>

Dies macht meine Anwendung verzögert (durchschnittlich 14 fps, Leerlaufzeit 51100 ms):

while(this.postService.hasPosts()){
  setTimeout(()=> {
   this.posts.push(this.postService.next(10));
  },100);
}

Dies macht meine Anwendung reibungslos (durchschnittlich fps 35, Leerlaufzeit 40800ms)

while(this.postService.hasPosts()){
  timer(100).subscribe(()=> {
    this.posts.push(this.postService.next(10));
  });
}

Gibt es eine Erklärung, warum der rxjs-Timer so viel besser funktioniert?

Ich habe eine Laufzeitanalyse mit Firefox durchgeführt. Im ersten Beispiel sinkt die Bildrate auf 14 fps. Im anderen Beispiel auf 35 fps.

Auch die Leerlaufzeit ist 20% kürzer.

Diese Methode ist noch flüssiger (durchschnittlich fps 45, Leerlaufzeit 13500 ms):

interval(100).pipe(takeWhile(this.postService.hasPosts()).subscribe(()=> {
    this.posts.push(this.postService.next(10));
  });
}
Luxusproblem
quelle

Antworten:

2

Ihre letzte Lösung ist die einzig richtige.

Die beiden anderen Lösungen sollten nicht so funktionieren, wie Sie es erwartet haben. Eigentlich sollte dies zu einer Endlosschleife führen.

Dies liegt daran, wie der Eventloop von JavaScript funktioniert. Das folgende Bild zeigt ein Modell der JavaScript-Laufzeit (Bild wurde von hier aufgenommen ):

Geben Sie hier die Bildbeschreibung ein

Die für uns relevanten Teile sind die stackund die queue. Eine JavaScript-Laufzeit verarbeitet die Nachrichten auf dem queue. Jede Nachricht ist einer Funktion zugeordnet, die beim Verarbeiten der Nachricht aufgerufen wird.

Für den Stapel erstellt jeder Funktionsaufruf einen Rahmen auf dem Stapel, der die Funktionsargumente und lokalen Variablen enthält. Wenn eine Funktion eine andere Funktion aufruft, wird ein neuer Frame auf den Stapel geschoben. Wenn eine Funktion zurückkehrt, wird der obere Frame aus dem Stapel entfernt.

Wenn der Stapel leer ist, verarbeitet die JavaScript-Laufzeit die nächste Nachricht auf der queue(ältesten).

Wenn Sie verwenden setTimeout(() => doSomething(),100), wird die doSomething()Funktion nach 100 Millisekunden zur Warteschlange hinzugefügt. Dies ist der Grund, warum die 100 Millisekunden keine garantierte Zeit, sondern eine minimale Zeit sind. Daher wird Ihr doSomething methodnur aufgerufen, wenn der Stapel leer ist und sich nichts anderes in der Warteschlange befindet.

Da Sie jedoch in einer while-Schleife iterieren und Ihr Zustand vom Code in Ihrer Schleife abhängt setTimeout, haben Sie eine Endlosschleife erstellt, da der Stapel nicht leer wird und Ihr this.posts.push(this.postService.next(10));Code daher niemals aufgerufen wird.

Für die RxJS-Implementierungen gilt das Gleiche. Sie verwenden Scheduler, um das Timing zu handhaben. Es gibt verschiedene interne Scheduler-Implementierungen in RxJS, aber wie wir in den Implementierungen für intervalund sehen können timer, ist die Standardeinstellung der asyncScheduler, wenn wir keinen Scheduler angeben. Die asyncScheduler-Zeitpläne setIntervalfunktionieren wie setTimeoutoben beschrieben und senden eine weitere Nachricht in die Warteschlange.

Ich habe Ihre beiden Lösungen mit der while-Schleife ausprobiert und tatsächlich hat die erste meinen Browser völlig eingefroren, während die zweite sehr verzögert war, aber innerhalb der while-Schleife etwas an die Konsole ausgeben konnte. Ich weiß eigentlich nicht, warum der zweite etwas performanter ist, aber trotzdem sind beide nicht das, was Sie eigentlich wollen. Sie haben bereits eine gute Lösung gefunden, und ich hoffe, diese Antwort hilft Ihnen zu verstehen, warum die ersten Lösungen so schlecht funktionieren.

Max K.
quelle