Pourquoi setTimeout () rend mon application retardée, mais Rxjs timer (). Subscribe (…) ne le fait pas?

9

J'ai un composant, qui "charge paresseusement" quelques commentaires, à un intervalle de 100 ms.

Quand j'utilise setTimeout, c'est vraiment lent.

composant

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

Cela rend mon application lente (fps 14 moyenne, temps d'inactivité 51100 ms):

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

Cela rend mon application fluide (moyenne fps 35, temps d'inactivité 40800ms)

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

Y a-t-il une explication, pourquoi le temporisateur rxjs, fonctionne-t-il beaucoup mieux?

J'ai fait une analyse d'exécution avec Firefox. Dans le premier exemple, la fréquence d'images tombe à 14 ips Dans l'autre exemple, 35 ips.

Même le temps d'inactivité est inférieur de 20%.

Cette méthode est encore plus fluide (fps 45 en moyenne, 13500 ms d'inactivité):

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

Réponses:

2

Votre dernière solution est la seule correcte.

Les deux autres solutions ne devraient pas fonctionner comme vous vous attendiez à ce qu'elles fonctionnent. En fait, cela devrait entraîner une boucle infinie.

Cela est dû au fonctionnement de la boucle d' événements JavaScript . L'image suivante montre un modèle du runtime JavaScript (l'image a été prise à partir d' ici ):

entrez la description de l'image ici

Les parties pertinentes pour nous sont le stacket le queue. Un runtime JavaScript traite les messages sur le queue. Chaque message est associé à une fonction qui est appelée lors du traitement du message.

Pour la pile, chaque appel de fonction crée un cadre sur la pile qui contient les arguments des fonctions et les variables locales. Si une fonction appelle une autre fonction, un nouveau cadre est poussé au-dessus de la pile. Lorsqu'une fonction revient, le cadre supérieur est sorti de la pile.

Maintenant, si la pile est vide, le runtime JavaScript traitera le message suivant sur le queue(le plus ancien).

Si vous utilisez setTimeout(() => doSomething(),100), la doSomething()fonction est ajoutée à la file d'attente après 100 millisecondes. C'est la raison pour laquelle les 100 millisecondes ne sont pas un temps garanti mais un temps minimal. Par conséquent, votre doSomething methodseul est appelé, si la pile est vide et que rien d'autre n'est dans la file d'attente.

Mais comme vous itérez dans une boucle while et que votre condition dépend du code à l'intérieur de votre setTimeout, vous avez créé une boucle infinie car la pile ne sera pas vide et donc votre this.posts.push(this.postService.next(10));code ne sera jamais appelé.

Pour les implémentations RxJS, la même chose est vraie. Ils utilisent des planificateurs pour gérer le timing. Il existe différentes implémentations de planificateur interne dans RxJS, mais comme nous pouvons le voir dans les implémentations de intervalet timer, si nous ne spécifions pas de planificateur, la valeur par défaut est asyncScheduler. Les planifications asyncScheduler fonctionnent avec setIntervalce qui fonctionne comme setTimeoutmentionné ci-dessus et poussent un autre message dans la file d'attente.

J'ai essayé vos deux solutions avec la boucle while et en fait la première a complètement gelé mon navigateur tandis que la seconde était super laggy mais pouvait sortir quelque chose vers la console à l'intérieur de la boucle while. En fait, je ne sais pas pourquoi le second est un peu plus performant, mais néanmoins les deux ne sont pas ce que vous voulez réellement. Vous avez déjà trouvé une bonne solution et j'espère que cette réponse vous aidera à comprendre pourquoi les premières solutions fonctionnent si mal.

Max K
la source