Javascript:避免急切的异步执行,并在事件循环上优先调用。

问题描述 投票:0回答:1

我的Javascript应用需要在UI线程上做一些繁重的计算(在本问题的范围内,我们忽略web工作者)。幸运的是,要做的工作是尴尬的并行工作,可以切成成千上万的小异步函数。

我想做的是。把所有这些异步函数排在事件循环上 但优先处理UI事件 如果这样做可行,对UI响应速度的影响应该很低。每个函数都只需要几毫秒的时间。

问题是Javascript热衷于执行异步代码。直到你遇到这样的事情 await IO 它只是简单地执行所有的代码,就像它是同步的一样。

我想打破这个局面。当执行遇到一个异步调用时,它应该把它放在事件循环中, 但在执行它之前,它应该检查是否有一个UI事件等待处理。基本上,我正在寻找一种方法,可以立即实现 yield 的函数,但没有返回结果。

我试验了一下,找到了一个变通的办法。不直接调用async函数,而是用超时来包装。这几乎就是我想要的。异步调用不会被执行,而是在事件循环中排队。下面是一个简单的例子,基于react的。点击后,按钮会立即更新,而不是等待所有这些计算完成。(这个例子可能看起来像可怕的代码,我在渲染函数里面放了大量的计算。但这只是为了让它简短。其实异步函数在哪里被踢掉并不重要,它们总是阻塞单一的UI线程)。)

// timed it, on my system it takes a few milliseconds
async function expensiveCalc(n: number) {
  for (let i = 0; i < 100000; i++) {
    let a = i * i;
  }
  console.log(n);
}

const App: React.FC = () => {
  const [id, setId] = useState("click me");

  // many inexpensive calls that add up
  for (let i = 0; i < 1000; i++) {
    setTimeout(() => expensiveCalc(i), 1);
  }

  // this needs to be pushed out as fast as possible
  return (
    <IonApp>
      <IonButton
        onClick={() => setId(v4())}>
        {id}
      </IonButton>
    </IonApp>
  );
};

现在,这看起来很整洁 渲染函数的同步执行永远不会被中断。所有的计算函数都是在渲染完成后排队完成的,它们甚至不需要异步。

我不明白的是:即使你点击了很多次按钮,这也能正常工作!我的期望是,事件循环上的所有调用都被同等对待。所以第一次渲染应该是即时的,第二次需要等待所有的超时先执行。但我看到的是

  • 点击按钮 -> 文本更新
  • 立即再次点击按钮 -> 文字更新了。
  • 看控制台日志:它计数到1000,然后从0开始,再计数到1000。

所以事件循环没有按顺序执行。而且不知为何UI事件会被优先处理。这真是太棒了。但它是如何工作的呢?我可以自己确定事件的优先级吗?

这里有一个沙盒,你可以玩玩这个例子。

javascript asynchronous browser event-loop
1个回答
3
投票

Bergi的回答 有一个很好的解释(浏览器的事件循环中有任务优先级)。

请注意,我们可能在不久的将来1 拥有 API 来处理这个问题,我们自己作为网络开发人员。

但我必须注意到,你最初愿意做的事情似乎正是你的工作。requestIdleCallback 被设计为:只有在事件循环中没有其他事情发生时才执行回调。

btn.onclick = (e) => {
  for (let i = 0; i<100000; i++) {
    requestIdleCallback( () => {
      log.textContent = 'executed #' + i;
    });
  }
  console.log( 'new batch' );
}
<pre id="log"></pre>
<button id="btn">click me</button>

这样你就不会依赖未定义的行为,这些都是由实现来决定的,这里规范要求这种行为。



1. 这个API已经可以在Chrome浏览器中测试,通过打开 chrome:/flags#enable-experimental-web-platform-features.


2
投票

所以事件循环并没有按顺序执行。而且不知为何UI事件会被优先处理。这真是太棒了。但它是如何工作的呢?

浏览器的事件循环是一个复杂的怪兽,它包含了不同 "任务源 "的多个 "任务队列 "和浏览器。它确实包含多个针对不同 "任务源 "的 "任务队列",浏览器可以自由优化它们之间的调度。然而,每个队列本身确实是遵循顺序的。

那我可以自己安排事件的优先级吗?

不可以--如果不编写自己的调度程序并在事件循环中运行,就不能。

© www.soinside.com 2019 - 2024. All rights reserved.