Perché setTimeout () rende la mia app ritardata, ma Rxjs timer (). Iscriviti (...) no?


9

Ho un componente, che "carica pigro" alcuni commenti, a intervalli di 100 ms.

Quando uso setTimeout, è davvero in ritardo.

componente

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

Questo rende la mia applicazione ritardata (avg fps 14, tempo di inattività 51100ms):

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

Questo rende la mia applicazione fluida (avg fps 35, tempo di inattività 40800ms)

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

C'è qualche spiegazione, perché il timer rxjs funziona molto meglio?

Ho fatto un'analisi di runtime con Firefox. Nel primo esempio, la frequenza dei fotogrammi scende a 14 fps Nell'altro esempio, 35 fps.

Anche il tempo di inattività è inferiore del 20%.

Questo metodo è ancora più fluido (avg fps 45, tempo di inattività 13500ms):

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

Risposte:


2

La tua ultima soluzione è l'unica corretta.

Le altre due soluzioni non dovrebbero funzionare come previsto. In realtà, ciò dovrebbe comportare un ciclo infinito.

Ciò è dovuto al funzionamento di eventloop di JavaScript . L'immagine seguente mostra un modello del runtime JavaScript (L'immagine è stata presa da qui ):

inserisci qui la descrizione dell'immagine

Le parti rilevanti per noi sono il stacke il queue. Un runtime JavaScript elabora i messaggi suqueue . Ogni messaggio è associato a una funzione che viene chiamata durante l'elaborazione del messaggio.

Per lo stack, ogni chiamata di funzione crea un frame nello stack che contiene gli argomenti delle funzioni e le variabili locali. Se una funzione chiama un'altra funzione, viene inserito un nuovo frame in cima allo stack. Quando una funzione ritorna, il frame superiore viene estratto dallo stack.

Ora se lo stack è vuoto il runtime JavaScript elaborerà il messaggio successivo sul queue(il più vecchio).

Se si utilizza setTimeout(() => doSomething(),100), la doSomething()funzione viene aggiunta alla coda dopo 100 millisecondi. Questo è il motivo per cui i 100 millisecondi non sono un tempo garantito ma un tempo minimo. Quindi doSomething methodviene chiamato solo se lo stack è vuoto e nient'altro è in coda.

Ma mentre stai iterando in un ciclo while e la tua condizione dipende dal codice all'interno del tuo setTimeout, hai creato un ciclo infinito perché lo stack non si svuoterà e quindi il tuo this.posts.push(this.postService.next(10));codice non verrà mai chiamato.

Per le implementazioni RxJS lo stesso vale. Usano gli scheduler per gestire i tempi. Esistono diverse implementazioni dello scheduler interno in RxJS, ma come possiamo vedere nelle implementazioni per intervale timer, se non specifichiamo uno scheduler, quello predefinito è asyncScheduler. Le pianificazioni asyncScheduler funzionano con le setIntervalquali funzionano come setTimeoutmenzionato sopra e inseriscono un altro messaggio in coda.

Ho provato le tue due soluzioni con il ciclo while e in realtà il primo ha bloccato completamente il mio browser, mentre il secondo era molto lento ma poteva inviare qualcosa alla console all'interno del ciclo while. In realtà non so perché il secondo sia un po 'più performante, ma entrambi non sono quello che vuoi davvero. Hai già trovato una buona soluzione e spero che questa risposta possa aiutarti a capire perché le prime soluzioni funzionano così male.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.