繁体   English   中英

哪个任务(setTimeout 或点击事件)在任务队列中被优先处理?

[英]Which task(setTimeout or click event) is prioritized in the task queue?

我正在学习执行堆栈、任务队列和事件循环机制。

我连续点击按钮直到主线程可用(就在函数 a() 完成之前),如下所示。
我认为click(UI) events 和setTimeout 使用相同的队列,称为Macrotask 或Task Queue,所以当我单击1s 和3s 之间的按钮时,我认为task2 的日志打印在task1 和task2 之间。 但结果并非如此。 Task2(click event) 总是先打印,setTimeout events(task1,task3) 在单击事件之后打印。

所以我想知道点击事件是否使用了与 setTimeout 不同的队列机制,或者点击事件的优先级高于 setTimeout。

预先感谢您的帮助

手术

  1. 单击按钮(任务 2)
  2. --------1000ms setTimeout task1--------
  3. 单击按钮(任务 2)
  4. --------3000ms setTimeout task3--------
  5. 单击按钮(任务 2)
  6. --------6000ms 主线程现已可用--------

我的期望日志顺序

fn a done
task2 (click) done
task1 (setTimeout 1000ms) done
task2 (click) done
task3 (setTimeout 3000ms) done
task2 (click) done

结果日志顺序

fn a done
task2 (click) done
task2 (click) done
task2 (click) done
task1 (setTimeout 1000ms) done
task3 (setTimeout 3000ms) done

代码

 const btn = document.querySelector('button'); btn.addEventListener('click', function task2() { console.log('task2 (click) done'); }); function a() { setTimeout(function task1() { console.log('task1 (setTimeout 1000ms) done'); }, 1000); setTimeout(function task3() { console.log('task3 (setTimeout 3000ms) done'); }, 3000); // hold main thread for 6000ms(*1) const startTime = new Date(); while (new Date() - startTime < 6000); console.log('fn a done'); } a();
 <button>button</button> <script src="main.js"></script>

让我们来看看你用 setTimeout 和 while 循环做了什么。

如果您运行代码片段,您将看到 1 秒和 3 秒超时的时间戳基本相同。 这是因为 while 循环会阻塞主线程 6 秒,直到它可以运行 setTimeout 中的函数为止。

回调不会在 1 秒或 3 秒后执行,而是仅在主线程空闲时执行。 setTimeout 保证回调函数在最少 x 毫秒后执行,任务 1 >= 1 秒,任务 3 >= 3 秒。

 function a() { setTimeout(function task1() { console.log('task1 (setTimeout 1000ms) done ' + new Date()); }, 1000); setTimeout(function task3() { console.log('task3 (setTimeout 3000ms) done ' + new Date()); }, 3000); // hold main thread for 6000ms(*1) const startTime = new Date(); while (new Date() - startTime < 6000); console.log('fn a done'); } a();

我认为 click(UI) 事件和 setTimeout 使用相同的队列

他们没有。

UI 事件使用用户交互(UI)任务源,它在大多数浏览器中都有自己的任务队列setTimeout使用计时器任务源,它在大多数浏览器中也有自己的任务队列

尽管规范没有要求,但 UI 任务源在几乎所有浏览器中的所有任务源中具有最高优先级之一,而计时器具有最低优先级之一。

这种优先级的工作原理是,在事件循环处理模型的第一步,用户代理(UA)必须在其事件队列之一中选择它将执行哪个任务

注意:通俗地称为“宏任务”的是任何不是任务的任务。
微任务队列不是任务队列,虽然可以在事件循环处理的第一步中选择微任务作为主任务,但微任务队列不能优先,因为它必须在每个微任务检查点同步清空,这可能在每次事件循环迭代期间发生多次,尤其是在执行所选任务之后

所以在这里,当while循环完成阻塞事件循环,并且下一次迭代开始时,UA 将不得不选择它应该从哪个任务队列中选择下一个任务。 它将在其UI 任务队列中看到有新事件在等待,并执行它们,因为它们具有更高的优先级。
然后当它们全部执行完毕时,它会选择定时器队列并按照它们的预定时间顺序执行它们。

还要注意, 他们有一个饥饿系统,可以防止高优先级任务队列阻塞其他任务队列太长时间。


最后,我应该提一下,有一个建议让我们的 web 开发人员直接处理所有这些优先事项: 主线程调度

使用这个实验性功能,我们可以将代码片段重写为

 if( !("scheduler" in window) ) { console.error("Your browser doesn't support the postTask API"); console.error("Try enabling the Experimental Web Platform features in chrome://flags"); } else { scheduler.postTask(() => { console.log('task1 (background) done'); }, { priority: "background" } ); scheduler.postTask(() => { console.log('task2 (background) done'); }, { priority: "background" } ); // hold main thread for 6000ms(*1) const startTime = new Date(); while (new Date() - startTime < 2000); scheduler.postTask(() => { console.log('task3 (user-blocking) done'); }, { priority: "user-blocking" } ); console.log('synchronous done'); }

并看到最后的任务是先执行的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM