Por que setTimeout () torna meu aplicativo lento, mas o Rxjs timer (). Subscribe (…) não?

9

Eu tenho um componente, que "carrega preguiçosamente" alguns comentários, no intervalo de 100ms.

Quando eu uso o setTimeout, é realmente lento.

componente

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

Isso torna meu aplicativo lento (avg fps 14, tempo ocioso 51100ms):

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

Isso torna meu aplicativo suave (avg fps 35, tempo ocioso 40800ms)

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

Existe alguma explicação, por que o timer rxjs funciona muito melhor?

Eu fiz uma análise de tempo de execução com o Firefox. No primeiro exemplo, a taxa de quadros cai para 14 qps. No outro exemplo, 35 qps.

Até o tempo ocioso é 20% menor.

Esse método é ainda mais suave (avg fps 45, tempo ocioso 13500ms):

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

Respostas:

2

Sua última solução é a única correta.

As outras duas soluções não devem funcionar como você esperava que funcionassem. Na verdade, isso deve resultar em um loop infinito.

Isso ocorre porque o eventloop do JavaScript funciona. A figura a seguir mostra um modelo do tempo de execução JavaScript (a imagem foi tirada daqui ):

insira a descrição da imagem aqui

As partes relevantes para nós são o stacke o queue. Um tempo de execução JavaScript processa as mensagens no queue. Cada mensagem é associada a uma função que é chamada à medida que a mensagem é processada.

Para a pilha, cada chamada de função cria um quadro na pilha que contém os argumentos das funções e variáveis ​​locais. Se uma função chama outra função, um novo quadro é empurrado para cima da pilha. Quando uma função retorna, o quadro superior é retirado da pilha.

Agora, se a pilha estiver vazia, o tempo de execução do JavaScript processará a próxima mensagem na queue(a mais antiga).

Se você usar setTimeout(() => doSomething(),100), a doSomething()função será adicionada à fila após 100 milissegundos. Esta é a razão pela qual os 100 milissegundos não são um tempo garantido, mas um tempo mínimo. Portanto, você doSomething methodsó é chamado se a pilha estiver vazia e não houver mais nada na fila.

Mas como você está iterando em um loop while e sua condição depende do código dentro do seu setTimeout, você criou um loop infinito porque a pilha não fica vazia e, portanto, seu this.posts.push(this.postService.next(10));código nunca será chamado.

Para as implementações do RxJS, o mesmo é verdadeiro. Eles usam agendadores para lidar com o tempo. Existem diferentes implementações do agendador interno no RxJS, mas como podemos ver nas implementações de intervale timer, se não especificarmos um agendador, o padrão é o asyncScheduler. Os agendamentos asyncScheduler funcionam com os setIntervalquais funcionam como setTimeoutmencionado acima e enviam outra mensagem à fila.

Eu tentei suas duas soluções com o loop while e, na verdade, a primeira congelou totalmente meu navegador enquanto a segunda estava super atrasada, mas poderia gerar algo para o console dentro do loop while. Na verdade, não sei por que o segundo é um pouco mais eficiente, mas, no entanto, ambos não são o que você realmente deseja. Você já encontrou uma boa solução e espero que esta resposta possa ajudá-lo a entender por que as primeiras soluções são tão ruins.

Max K
fonte