经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Java » 查看文章
setTimeout(fn, 0) // it works - JavaScript 事件循环 动画演示
来源:cnblogs  作者:AaronLin  时间:2024/4/17 15:29:34  对本文有异议

在前端代码中很经常看到使用 setTimeout(fn, 0),如下面代码所示,乍一看很多余,但是移除了可能会出现一些奇奇怪怪的问题。要解释这个就需要理解 事件循环(Event Loop),下面会通过一些例子和动画来辅助理解事件循环

  1. setTimeout(() => {
  2. // 调用一些方法
  3. }, 0)

为什么使用事件循环

JS 是单线程的(浏览器和 Node则是多线程的),为了避免 渲染主线程 阻塞,需要异步,事件循环 是异步的实现方式

浏览器在一个渲染主线程中运行一个页面中的所有 JavaScript 脚本,以及呈现布局,回流,和垃圾回收。为了避免 同步 的执行方式导致渲染主线程阻塞,使得页面卡死,所以浏览器采用异步的方式:渲染主线程将任务交给其他线程去处理,自身 立即结束 任务的执行,转而执行后续代码,当其他线程完成时,将事先传递的回调函数包装成任务,加入到对应的消息队列的末尾排队,等待渲染主线程调度执行

流程:

  1. 渲染主线程执行全局 JS,需要异步的任务放到对应的队列,如果是 setTimeout 则会有线程计时,到了指定时间会将任务放入 延时队列(并非立即执行)
  2. 渲染主线程为空时,按队列的优先级依次选择队列(最先执行微队列的任务),依次按顺序执行各个队列的任务

任务没有优先级,而消息队列有优先级,不同任务分属于不同队列:参考 W3C 规范微队列优先级最高,接着是交互队列然后才是延时队列

常见队列:

  • 微队列(microtask):?户存放需要最快执?的任务,优先级「最?」,通过 Promise.resolve().then() ?即把?个函数添加到微队列
  • 交互队列:?于存放?户操作后产?的事件处理任务,优先级「?」
  • 延时队列:?于存放计时器到达后的回调任务,优先级「中」

事件循环

下面例子来自于:《WEB前端大师课》,大块的文字描述相对没那么直观,所以用 Keynote 做了 gif 方便理解(如果有更好的做 gif 的方式可以留言告诉我)

1. JS阻碍页面渲染

JS 修改了 DOM 后,并不会马上显示在页面上,需要进行 绘制 后才会显示页面变更

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head></head>
  4. <body>
  5. <h1>初始h1</h1>
  6. <button>change</button>
  7. <script>
  8. var h1 = document.querySelector('h1');
  9. var btn = document.querySelector('button');
  10. function delay(duration) {
  11. var start = Date.now();
  12. while (Date.now() - start < duration) {}
  13. }
  14. btn.onclick = function () {
  15. h1.textContent = '修改h1 textContent';
  16. delay(3000);
  17. };
  18. </script>
  19. </body>
  20. </html>

01绘制任务

效果:点击 change 后,页面卡死,3s 后 h1 内容变更为:修改h1 textContent

2. 延迟队列

setTimeout 到达指定时间可能并不会立即执行

  1. setTimeout(function () {
  2. console.log(1);
  3. }, 0);
  4. function delay(duration) {
  5. var start = Date.now();
  6. while (Date.now() - start < duration) {}
  7. }
  8. delay(3000);
  9. console.log(2);

02settimeout

效果:卡死 3s 后输出 2 1

3. 微队列

使用 Promise.resolve().then 可以将任务直接添加到微队列

  1. setTimeout(function () {
  2. console.log(1);
  3. }, 0);
  4. Promise.resolve().then(function () {
  5. console.log(2);
  6. });
  7. console.log(3);

03微队列

效果:依次输出 3 2 1

4. 复杂情况

  1. function a() {
  2. console.log(1);
  3. Promise.resolve().then(function () {
  4. console.log(2);
  5. });
  6. }
  7. setTimeout(function () {
  8. console.log(3);
  9. Promise.resolve().then(a);
  10. }, 0);
  11. Promise.resolve().then(function () {
  12. console.log(4);
  13. });
  14. console.log(5);

04复杂Promise

效果:依次输出 5 4 3 1 2

拓展

理解了上面的概念,可以尝试分析一下 现代 JavaScript 教程 事件循环例子,检查一下是否理解了事件循环

参考资料

2024 年我还在写这样的代码
为什么 JS 要加入 setTimeout, css 的 transition 才能生效
深入理解和使用 Javascript 中的 setTimeout(fn,0)
主线程
并发模型与事件循环
异步 JavaScript
调度:setTimeout 和 setInterval

原文链接:https://www.cnblogs.com/aaronlinv/p/18139180

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号