为什么 setTimeout() 会使我的应用程序滞后,但 Rxjs timer().subscribe(...) 不会?

Lux*_*lem 9 javascript rxjs typescript angular

我有一个组件,它以 100 毫秒的间隔“延迟加载”一些评论。

当我使用 setTimeout 时,它真的很滞后。

成分

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

Run Code Online (Sandbox Code Playgroud)

这使我的应用程序滞后(平均 fps 14,空闲时间 51100 毫秒):

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

Run Code Online (Sandbox Code Playgroud)

这使我的应用程序流畅(平均 fps 35,空闲时间 40800 毫秒)

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

Run Code Online (Sandbox Code Playgroud)

有什么解释,为什么 rxjs 计时器工作得更好?

我用 Firefox 做了一个运行时分析。在第一个示例中,帧速率降至 14 fps。在另一个示例中,帧速率降至 35 fps。

甚至空闲时间也减少了 20%。

这个方法更流畅(平均fps 45,空闲时间13500ms):

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

Run Code Online (Sandbox Code Playgroud)

小智 2

您的最后一个解决方案是唯一正确的解决方案。

其他两个解决方案不应像您期望的那样工作。实际上,这应该会导致无限循环。

这是因为 JavaScript 的事件循环的工作原理。下图显示了 JavaScript 运行时的模型(图片取自此处):

在此输入图像描述

对我们来说相关的部分是stackqueue。JavaScript 运行时处理queue. 每条消息都与一个函数关联,该函数在处理消息时被调用。

对于堆栈,每个函数调用都会在堆栈上创建一个帧,其中包含函数参数和局部变量。如果一个函数调用另一个函数,则会将新帧推送到堆栈顶部。当函数返回时,顶部框架将从堆栈中弹出。

现在,如果堆栈为空,JavaScript 运行时将处理queue(最旧的)上的下一条消息。

如果您使用setTimeout(() => doSomething(),100),该doSomething()函数会在 100 毫秒后添加到队列中。这就是为什么 100 毫秒不是保证时间而是最小时间的原因。doSomething method因此,只有当堆栈为空且队列中没有其他内容时,您才会被调用。

但是,当您在 while 循环中迭代并且您的条件取决于 中的代码时setTimeout,您创建了一个无限循环,因为堆栈不会变空,因此您的this.posts.push(this.postService.next(10));代码永远不会被调用。

对于 RxJS 实现也是如此。他们使用调度程序来处理计时。intervalRxJS 中有不同的内部调度程序实现,但正如我们在和 的实现中看到的timer,如果我们不指定调度程序,则默认调度程序是 asyncScheduler。asyncScheduler 调度工作,setInterval其工作方式如上所述setTimeout,并将另一条消息推送到队列上。

我用 while 循环尝试了您的两个解决方案,实际上第一个解决方案完全冻结了我的浏览器,而第二个解决方案超级滞后,但可以在 while 循环内向控制台输出一些内容。我实际上不知道为什么第二个性能更高一点,但尽管如此,两者都不是您真正想要的。您已经想出了一个很好的解决方案,我希望这个答案可以帮助您理解为什么第一个解决方案表现如此糟糕。