setTimeout回调和Keypress事件是否不遵循事件队列中的FIFO?

时间:2019-05-27 23:17:59

标签: javascript settimeout task-queue event-loop

我正在进行实验以更好地了解JavaScript事件队列。

我有一个昂贵的操作,什么也不做,我只是用来占用主线程。持续约400毫秒。

然后我有一个带有keypress事件侦听器和onChange处理程序的输入,只要在输入中按下一个键,onChange处理程序就会运行此昂贵的操作。但是在执行昂贵的操作之前,它会设置setTimeout的延迟为0,并且可以跟踪一些回调。

我要做的是快速连续按下两个键。检查性能配置文件,我可以看到在第一个按键处理程序开始执行大约150毫秒后,第二个按键被注册。这意味着延迟为0的setTimeout回调应该已经完成​​其计时器并将其回调放入事件队列中。 keypress事件还应该将其处理程序回调放置在队列中,因为注册第二个keypress时,主线程仍在忙于第一个昂贵的操作。

因此,您可能希望,如果将超时回调放在第二个keypress事件之前首先放入事件队列中,则根据主线程的说法,一旦主线程完成了昂贵的操作,超时回调将首先运行。先进先出任务队列原则。

情况并非如此。按键事件排在第一位...

我正试图了解原因。我的猜测是不仅只有一个事件队列。诸如按键之类的用户事件具有与计时器事件不同的队列,并且JavaScript引擎优先于计时器来优先于用户事件。

这是一个复制此方案的codeSandbox,因此您可以自己测试结果。该代码也写在下面:https://codesandbox.io/s/r6vzp1qyq

你们认为发生了什么事?

function App() {
  const [timeoutTimestamps, setTimeoutTimestamp] = useState([]);
  const [keypressTimeStamps, setKeypressTimeStamp] = useState([]);

  //This expensive operation takes about 400 ms.
  function expensiveOperation() {
    const arr = new Array(30000000);
    arr.map(() => {
      arr.forEach(() => {
        arr.forEach(() => {});
      });
    });
  }

  function timerCallback() {
    const newTimestamp = Date.now();
    setTimeoutTimestamp(prevState => [...prevState, newTimestamp]);
  }

  function onKeypress() {
    const newTimestamp = Date.now();
    setTimeout(timerCallback, 0);
    expensiveOperation();
    setKeypressTimeStamp(prevState => [...prevState, newTimestamp]);
  }

  return (
    <div className="App">
      <input onChange={onKeypress} />
      <div className="container">
        <div className="time-stamps">
          <h3>Keypress TimeStamps</h3>
          {keypressTimeStamps.map(time => (
            <span>{time - keypressTimeStamps[0]}</span>
          ))}
        </div>

        <div className="time-stamps">
          <h3>Timeout TimeStamps</h3>
          {timeoutTimestamps.map(time => (
            <span>{time - keypressTimeStamps[0]}</span>
          ))}
        </div>
      </div>
    </div>
  );
}

此代码呈现以下时间戳记: enter image description here

您可以看到首先触发了第二个按键回调,然后触发了两个超时回调。

0 个答案:

没有答案