为同源CPU密集型iframe单独的事件循环

Separate event-loop for same-origin CPU intensive iframe

本文关键字:单独 事件 循环 iframe 密集型 CPU      更新时间:2023-09-26

假设我有一个CPU密集型iframe
它与父页面位于同一域,因此不适用跨域,因此它共享父页面的事件循环。

是否有可能有一个单独的JS上下文(包括一个单独的事件循环)为iframe,这样它就不会阻止父的UI?

iframe标签设置sandbox属性并不能解决这个问题,尽管文档声明iframe将被认为来自'唯一来源'。

这个问题是假设性的/概念性的。我没有一个真正的用例。只是好奇。
我正在徘徊像JSFiddle这样的网站是如何运行用户代码的,显然它是从不同域(jshell.net在JSFiddle的情况下)的iframe运行。

像JSFiddle这样的服务如何在不同的事件循环中运行用户代码?

他们并没有解决实际问题,他们只是通过使用跨域iframe来绕过它。

例如,这可以很容易地在StackBlitz上看到,他们为每个执行环境创建一个子域。

在JSFiddle上,这一开始有点难以理解,因为它们的iframe没有src属性集。然而,iframe被绑定到一个表单,它最终成为另一个跨域iframe。

Workaround - Web Workers

正如其他人正确提到的,你可以使用web workers来生成一个新的JS上下文,并在那里运行一些繁重的计算。

这个可以工作,因为web worker运行在隔离线程(MDN)中。

解决方法-在较小的块中运行代码

正如其他人提到的那样,通过在较小的块中运行代码可以提高性能。看下面的例子:

const array = [ ... ]; // array with hundreds of items
const result = [];
const stepByStepCalculation = () => {
  if (array.length) {
    result.push(doExpensiveCalculation(array[0]));
    array.shift();
    requestAnimationFrame(stepByStepCalculation);
  }
}
stepByStepCalculation();

现在,在事件循环的每次迭代之后,将一步一步地处理数组中的一项。

此外,使用requestAnimationFrame代替setIntervalsetTimeout有一个优点,它将在每次事件循环迭代之后被调用,而不是在~4ms之后,使其更快。

解决方法的问题

我们的变通方法只有在我们昂贵的任务不需要不断地从DOM中读取的情况下才有效。

这是一个大问题,如果我们的同源iframe有昂贵的JavaScript动画。它将严重干扰并可能导致UI延迟。

总结

将计算移到工作线程中,或者一步一步地执行以减少负载。

然而,对于你的主要问题,我们不能为同源iframe创建一个单独的JS上下文(截至目前)。在线JS shell通过使用跨域iframe来解决这个问题。

如果我们能以某种方式在合成器或光栅线程中运行JavaScript代码,这将是很有趣的,因为它们应该可以访问我们的DOM,不像web工作者。

Javascript是单线程的。在其他线程中执行任务的一种方式是网络工作者。但是它们不能访问DOM。

如果你有一个非常密集的任务阻塞了你的UI,你可以把这些任务划分成块,中间有超时。超时不会阻止其他代码的执行。所以,如果你足够聪明的话,这个技术可以用来为UI创建响应的时间窗口。

这个问题(和答案)处理的是一个非常相似的问题