'click' 之类的事件是宏任务吗?
Is events like 'click' are macrotasks?
代码:
setTimeout(() => console.log(1), 10);
for (let i = 0; i < 3e9; i++) {}
console.log(0);
window.onclick = () => console.log('click');
当这个脚本是运行:
如果我不 'click' 而第 2 行的同步代码 运行ning 控制台显示预期结果:
0
1
如果我 'click' 而第 2 行的同步代码 运行ning 控制台显示意外结果:
0
click
1
所以我有两个问题:
- 如果'click'是一个宏任务那么它应该在setTimeout之后进入宏任务回调队列吗?这样当同步代码执行完毕,栈为空,宏任务回调队列将console.log(0)入栈,栈执行,回调队列将console.log('click')入栈,堆栈执行它。
- 如果我在第 2 行的同步代码为 运行ning 时单击,为什么我在控制台中看到 'click'?我在执行器到达第 4 行之前单击了...
有多种 task-queues,它们都有不同的优先级,由 User-Agent (UA) 设置。
Event Loop's processing model 的第一步是从这些 tasks-queues 中选择一个任务,这是他们可以决定的,即使一个任务实际上已经 queued另一个,他们可以选择它,只要他们不 queued 在同一个 task-queue.
timer task source is often one of the less prioritized ones, and the UI task source 最优先的之一。
这就是您在这里遇到的情况。
请注意,有人提议 API 允许我们 web-devs 访问此优先级系统: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
老实说,任务优先级排序可能不是消息事件和“正常”任务在我的代码段中的“超时”之前触发的原因。 timer-initialisation-steps algorithm actually is itself asynchronous and asks the UAs to queue that task while in parallel(第 14 步)。所以这个任务 应该 无论如何是 queued 在其他同步 queued 的任务之后 queued。
还要注意大多数 UA (至少 Chromium 和 Firefox) 有一个饥饿系统,避免 queue 吃掉所有资源而不让另一个queues 永远执行他们的任务。
此外,为了回答问题的标题,事件不是任务,您可以很好地从同一任务同步触发事件(例如使用 EventTarget.dispatchEvent
)。
对于第二个项目符号问题,
"If I click when synchronous code from line 2 is running, why I see 'click' in console at all? I clicked before executor reached the line 4..."
这仅仅是因为 UA 将继续执行 JavaScript 作业直到它结束,甚至在 排队任务 之前将事件分派给它的所有目标并最终调用所有他们的回调。所以在那个时候,你的事件处理程序已经定义好了,它的回调也是如此。
代码:
setTimeout(() => console.log(1), 10);
for (let i = 0; i < 3e9; i++) {}
console.log(0);
window.onclick = () => console.log('click');
当这个脚本是运行:
如果我不 'click' 而第 2 行的同步代码 运行ning 控制台显示预期结果:
0 1
如果我 'click' 而第 2 行的同步代码 运行ning 控制台显示意外结果:
0 click 1
所以我有两个问题:
- 如果'click'是一个宏任务那么它应该在setTimeout之后进入宏任务回调队列吗?这样当同步代码执行完毕,栈为空,宏任务回调队列将console.log(0)入栈,栈执行,回调队列将console.log('click')入栈,堆栈执行它。
- 如果我在第 2 行的同步代码为 运行ning 时单击,为什么我在控制台中看到 'click'?我在执行器到达第 4 行之前单击了...
有多种 task-queues,它们都有不同的优先级,由 User-Agent (UA) 设置。
Event Loop's processing model 的第一步是从这些 tasks-queues 中选择一个任务,这是他们可以决定的,即使一个任务实际上已经 queued另一个,他们可以选择它,只要他们不 queued 在同一个 task-queue.
timer task source is often one of the less prioritized ones, and the UI task source 最优先的之一。
这就是您在这里遇到的情况。
请注意,有人提议 API 允许我们 web-devs 访问此优先级系统: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
老实说,任务优先级排序可能不是消息事件和“正常”任务在我的代码段中的“超时”之前触发的原因。 timer-initialisation-steps algorithm actually is itself asynchronous and asks the UAs to queue that task while in parallel(第 14 步)。所以这个任务 应该 无论如何是 queued 在其他同步 queued 的任务之后 queued。
还要注意大多数 UA (至少 Chromium 和 Firefox) 有一个饥饿系统,避免 queue 吃掉所有资源而不让另一个queues 永远执行他们的任务。
此外,为了回答问题的标题,事件不是任务,您可以很好地从同一任务同步触发事件(例如使用 EventTarget.dispatchEvent
)。
对于第二个项目符号问题,
"If I click when synchronous code from line 2 is running, why I see 'click' in console at all? I clicked before executor reached the line 4..."
这仅仅是因为 UA 将继续执行 JavaScript 作业直到它结束,甚至在 排队任务 之前将事件分派给它的所有目标并最终调用所有他们的回调。所以在那个时候,你的事件处理程序已经定义好了,它的回调也是如此。