¿Por qué setTimeout () hace que mi aplicación sea lenta, pero Rxjs timer (). Subscribe (...) no?

9

Tengo un componente, que "carga perezosamente" algunos comentarios, en el intervalo de 100 ms.

Cuando uso setTimeout, es realmente lento.

componente

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

Esto hace que mi aplicación sea lenta (avg fps 14, tiempo de inactividad 51100ms):

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

Esto hace que mi aplicación sea fluida (avg fps 35, tiempo de inactividad 40800 ms)

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

¿Hay alguna explicación, por qué el temporizador rxjs, funciona mucho mejor?

Hice un análisis de tiempo de ejecución con firefox. En el primer ejemplo, la velocidad de fotogramas cae a 14 fps. En el otro ejemplo, 35 fps.

Incluso el tiempo de inactividad es un 20% más bajo.

Este método es aún más uniforme (avg fps 45, tiempo de inactividad 13500 ms):

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

Respuestas:

2

Su última solución es la única correcta.

Las otras dos soluciones no deberían funcionar como esperaba que funcionaran. En realidad, esto debería resultar en un bucle infinito.

Esto se debe a cómo funciona el bucle de eventos de JavaScript . La siguiente imagen muestra un modelo del tiempo de ejecución de JavaScript (la imagen se tomó desde aquí ):

ingrese la descripción de la imagen aquí

Las partes relevantes para nosotros son el stacky el queue. Un tiempo de ejecución de JavaScript procesa los mensajes en el queue. Cada mensaje está asociado con una función que se llama a medida que se procesa el mensaje.

Para la pila, cada llamada a la función crea un marco en la pila que contiene los argumentos de las funciones y las variables locales. Si una función llama a otra función, se empuja un nuevo marco encima de la pila. Cuando una función regresa, el cuadro superior sale de la pila.

Ahora, si la pila está vacía, el tiempo de ejecución de JavaScript procesará el siguiente mensaje en queue(el más antiguo).

Si usa setTimeout(() => doSomething(),100), la doSomething()función se agrega a la cola después de 100 milisegundos. Esta es la razón por la cual los 100 milisegundos no son un tiempo garantizado sino un tiempo mínimo. Por lo tanto, doSomething methodsolo se llama si la pila está vacía y no hay nada más en la cola.

Pero como está iterando en un ciclo while y su condición depende del código dentro de usted setTimeout, ha creado un ciclo infinito porque la pila no se vaciará y, por lo tanto, su this.posts.push(this.postService.next(10));código nunca será llamado.

Para las implementaciones de RxJS, lo mismo es cierto. Usan programadores para manejar el tiempo. Hay diferentes implementaciones de planificador interno en RxJS, pero como podemos ver en las implementaciones para intervaly timer, si no especificamos un planificador, el predeterminado es el asyncScheduler. Los horarios de asyncScheduler funcionan con los setIntervalque funciona como se setTimeoutmencionó anteriormente, y envía otro mensaje a la cola.

Probé sus dos soluciones con el bucle while y, en realidad, la primera congeló por completo mi navegador, mientras que la segunda era súper lenta pero podía generar algo en la consola dentro del bucle while. De hecho, no sé por qué el segundo es un poco más eficiente, pero sin embargo, ambos no son lo que realmente quieres. Ya se te ocurrió una buena solución y espero que esta respuesta te ayude a entender por qué las primeras soluciones funcionan tan mal.

Max K
fuente