Чому setTimeout () робить моє додаток млявим, але таймер Rxjs (). Підписка (…) не робить?


9

У мене є компонент, який "ледачий завантажує" деякі коментарі, з інтервалом 100 мс.

Коли я використовую setTimeout, він дійсно млявий.

компонент

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

Це робить моє додаток малим (середній кадр в секунду 14, час очікування 51100 мс):

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

Це робить моє додаток гладким (середній кадр в секунду 35, час очікування 40800 мс)

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

Чи є пояснення, чому таймер rxjs працює так набагато краще?

Я робив аналіз часу з Firefox. У першому прикладі частота кадрів падає до 14 кадрів в секунду. В іншому прикладі - 35 кадрів в секунду.

Навіть час простою на 20% нижчий.

Цей спосіб ще більш плавний (середній кадр в секунду 45, час очікування 13500 мс):

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

Відповіді:


2

Ваше останнє рішення - єдино правильне.

Інші два рішення не повинні працювати так, як ви очікували, що вони працюватимуть. Власне, це повинно призвести до нескінченного циклу.

Це пов’язано з тим, як працює eventloop JavaScript . На наступному малюнку показана модель виконання JavaScript (Знімок зроблено звідси ):

введіть тут опис зображення

Відповідні для нас частини - це stackта queue. Виконання JavaScript обробляє повідомлення на queue. Кожне повідомлення асоціюється з функцією, яка викликається під час обробки повідомлення.

Для стека кожен виклик функції створює кадр на стеку, який містить аргументи функцій та локальні змінні. Якщо функція викликає іншу функцію, новий кадр висувається у верхній частині стека. Коли функція повертається, верхній кадр вискакує із стека.

Тепер, якщо стек порожній, час виконання JavaScript обробить наступне повідомлення на queue(найстарішому).

Якщо ви користуєтесь setTimeout(() => doSomething(),100), doSomething()функція додається до черги через 100 мілісекунд. Це причина, чому 100 мілісекунд - це не гарантований час, а мінімальний час. Тому ви doSomething methodтільки дзвоните, якщо стек порожній і нічого іншого не стоїть у черзі.

Але оскільки ви повторюєте цикл, і ваш стан залежить від коду всередині вашого setTimeout, ви створили нескінченний цикл, оскільки стек не порожній, і тому ваш this.posts.push(this.postService.next(10));код ніколи не буде викликаний.

Для реалізацій RxJS те ж саме. Вони використовують планувальники для обробки часу. У RxJS є різні внутрішні реалізації планувальника, але, як ми бачимо в реалізації для, intervalі timerякщо ми не вказамо планувальник, типовим є asyncScheduler. AsyncScheduler планує роботу, з setIntervalякою працює, як setTimeoutзгадувалося вище, і виштовхує інше повідомлення у черзі.

Я спробував ваші два рішення з циклом while і фактично перший повністю заморозив мій браузер, а другий був дуже млявим, але міг вивести щось на консоль всередині циклу while. Я насправді не знаю, чому другий трохи ефективніший, але все-таки обидва - це не те, чого ти насправді хочеш. Ви вже придумали хороше рішення, і я сподіваюся, що ця відповідь може допомогти вам зрозуміти, чому перші рішення діють так погано.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.