像“点击”这样的事件是宏任务吗?

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

代码:

setTimeout(() => console.log(1), 10);
for (let i = 0; i < 3e9; i++) {}
console.log(0);
window.onclick = () => console.log('click');

运行此脚本时:

  • 如果我在第 2 行同步代码运行时不“单击”,控制台会显示预期结果:

    0
    1
    
  • 如果我在第 2 行同步代码运行时“单击”控制台会显示意外结果:

    0
    click
    1
    

所以我有两个问题:

  1. 如果“click”是一个宏任务,那么它应该在setTimeout之后进入宏任务回调队列吗?这样,当同步代码完成时,堆栈为空,宏任务回调队列将console.log(0)放入堆栈,堆栈执行它,然后回调队列将console.log('click')放入堆栈,堆栈执行它。
  2. 如果我在第 2 行的同步代码运行时单击,为什么我会在控制台中看到“单击”?我在执行器到达第 4 行之前点击了...
javascript event-loop
2个回答
7
投票

有各种任务队列,它们都有不同的优先级,由用户代理(UA)设置。

事件循环处理模型的第一步是从这些任务队列中选择一个任务,在那里他们可以决定即使一个任务实际上已经在另一个任务之后排队,他们也可以选择它,因为只要它们不在同一个任务队列中排队即可。

计时器任务源通常是优先级较低的任务源之一,而UI任务源是优先级最高的任务源之一。

这就是你在这里所经历的。

请注意,有一个 API 提案允许我们的网络开发人员访问此优先级系统:https://github.com/WICG/main-thread-scheduling/blob/master/PrioritizedPostTask.md

setTimeout(() => console.log("timeout"), 0);

if( window.scheduler ) { // try to use the Prioritized postTask API
  scheduler.postTask(() => console.log("low priority task"), { priority: "background" });
  scheduler.postTask(() => console.log("normal priority task"), { priority: "user-visible" });
  scheduler.postTask(() => console.log("high priority task"), { priority: "user-blocking" });
}
else {
  console.log( "The Prioritized postTask API can be enabled from chrome://flags/#enable-experimental-web-platform-features" );
}

// queue on the message task-source (faster than timeout)
const { port1, port2 } = new MessageChannel();
port1.onmessage = () => console.log("message");
port2.postMessage("");

// block for 3 full seconds
const start = performance.now();
while( performance.now() - start < 3000 ) {}
console.log(0);

在 Chrome 中,设置了

chrome://flags/#enable-experimental-web-platform-features
标志,这会导致

0
high priority task
normal priority task
message
timeout
low priority task

百分百诚实地说,任务优先级可能不是导致消息事件和“正常”任务在我的代码片段中的“超时”之前触发的原因。 计时器初始化步骤算法实际上本身是异步的,并要求 UA 将该任务排队,同时并行(步骤 14)。因此,无论如何,这个任务应该排在同步排队的其他任务之后。

另请注意,大多数 UA (至少 Chromium 和 Firefox) 都有一个饥饿系统,避免队列吃掉所有资源而不让其他队列执行其任务。


此外,为了回答问题的标题,事件不是任务,您可以很好地从同一任务同步触发事件(例如使用

EventTarget.dispatchEvent
)。


对于第二个问题,

“如果我在第 2 行的同步代码运行时单击,为什么我会在控制台中看到“单击”?我在执行器到达第 4 行之前单击...”

这只是因为 UA 将继续执行 JavaScript 作业,直到其结束,然后才将事件分派到其所有目标并最终调用所有回调。所以到那时,你的事件处理程序就已经定义好了,它的回调也是如此。


0
投票

0 高优先级任务 暂停 正常优先任务 信息 低优先级任务

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.