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

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

为什么使用事件循环

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

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

流程:

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

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

常见队列:

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

事件循环

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

1. JS阻碍页面渲染

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

<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<h1>初始h1</h1>
<button>change</button>
<script>
var h1 = document.querySelector('h1');
var btn = document.querySelector('button'); function delay(duration) {
var start = Date.now();
while (Date.now() - start < duration) {}
} btn.onclick = function () {
h1.textContent = '修改h1 textContent';
delay(3000);
};
</script>
</body>
</html>

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

2. 延迟队列

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

setTimeout(function () {
console.log(1);
}, 0); function delay(duration) {
var start = Date.now();
while (Date.now() - start < duration) {}
} delay(3000); console.log(2);

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

3. 微队列

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

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

效果:依次输出 3 2 1

4. 复杂情况

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

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

拓展

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

参考资料

2024 年我还在写这样的代码

为什么 JS 要加入 setTimeout, css 的 transition 才能生效

深入理解和使用 Javascript 中的 setTimeout(fn,0)

主线程

并发模型与事件循环

异步 JavaScript

调度:setTimeout 和 setInterval

setTimeout(fn, 0) // it works - JavaScript 事件循环 动画演示的更多相关文章

  1. JavaScript下的setTimeout(fn,0)意味着什么?

    近期在研究异步编程的我对于setTimeout之类的东西异常敏感.在SegmentFault上看到了一个问题<关于SetTimeout时间设为0时>:提问者读了一篇文章,原文解释setTi ...

  2. 【 js 基础 】【 源码学习 】 setTimeout(fn, 0) 的作用

    在 zepto 源码中,$.fn 对象 有个 ready 函数,其中有这样一句 setTimeout(fn,0); $.fn = { ready: function(callback){ // don ...

  3. setTimeout(fn, 0) 的作用

    在 zepto 源码中,$.fn 对象 有个 ready 函数,其中有这样一句 setTimeout(fn,0); 1 $.fn = { 2 ready: function(callback){ 3 ...

  4. setTimeout(fn, 0)引发的JavaScipt线程的思考

    起因 周五改一个checkbox的display属性被错误地设置为none的bug. 经debug发现, 有两个地方修改了display属性: 1) checkbox的controller; 2) c ...

  5. c#封装DBHelper类 c# 图片加水印 (摘)C#生成随机数的三种方法 使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象 c# 制作正方形图片 JavaScript 事件循环及异步原理(完全指北)

    c#封装DBHelper类   public enum EffentNextType { /// <summary> /// 对其他语句无任何影响 /// </summary> ...

  6. 关于setTimeout(fn,0)

    JS是单线程引擎:它把任务放到队列中,不会同步去执行,必须在完成一个任务后才开始另外一个任务. 浏览器的内核是多线程的,它们在内核制控下相互配合以保持同步,一个浏览器至少实现三个常驻线程:javasc ...

  7. setTimeout(fn,0)的作用分析

    众所周知,大家对setTimeout的用法肯定都比较熟悉了,但是不是还是会经常忘记使用呢,例如博主阿里面试时就忘了,见阿里前端面试. 今天跟大家讨论一下setTimeout(fn,0)的用法,相信很多 ...

  8. 一篇文章图文并茂地带你轻松学完 JavaScript 事件循环机制(event loop)

    JavaScript 事件循环机制 (event loop) 本篇文章已经默认你有了基础的 ES6 和 javascript语法 知识. 本篇文章比较细致,如果已经对同步异步,单线程等概念比较熟悉的读 ...

  9. JavaScript 事件循环

    JavaScript 事件循环 事件循环 任务队列 async/await 又是如何处理的呢 ? 定时器问题 阻塞还是非阻塞 实际应用案例 拆分 CPU 过载任务 进度指示 在事件之后做一些事情 事件 ...

  10. setTimeout(fn,0)

    我们都知道setTimeout是一个延迟执行的函数 console.log(); setTimeout(function(){console.log();},); console.log(); 会得到 ...

随机推荐

  1. Swoft - Bean

    一.Bean 在 Swoft 中,一个 Bean 就是一个类的一个对象实例. 它(Bean)是通过容器来存放和管理整个生命周期的. 最直观的感受就是省去了频繁new的过程,节省了资源的开销. 二.Be ...

  2. Mysql 增删改查语言系列

    Mysql 数据语言系列 目录 Mysql 数据语言系列 一.数据定义语言 DDL 1 数据库规范 2 DDL 语言使用 2 创建视图 二. 数据操纵语言 DML 1 插入语法 2 更新语法 3 删除 ...

  3. Mybatis分页插件有效范围

    一.问题由来 在修改了一段代码后,将修改后的功能放在测试环境简单测试后,发现没有任何问题,因为测试环境数据量非常少(10条以下),因此 也就没有怀疑修改的代码存在问题,直接上生产环境,测试的时候发现后 ...

  4. vue 打包 可以加 --watch 持续打包 - 开发本地应用程序 - 套壳模式开发

    vue 打包 可以加 --watch 持续打包 - 开发本地应用程序 - 套壳模式开发 "watch": "vue-cli-service build --watch - ...

  5. 线上机器 swap 过高导致告警

    哈喽大家好,我是咸鱼. 今天收到了一个告警,说有台服务器上的 swap 过高,已经用了 50% 以上了. 登录机器查看一下内存以及 swap 的使用情况. [root@localhost ~]# fr ...

  6. C++自定义比较函数的bug

    auto cmp = [] (int x, int y) {return true;}; priority_queue<int, vector<int> , cmp> q; 报 ...

  7. GDB简洁指南

    启动gdb:gdb program 设置运行参数:set args 10 20 30 启动程序: run(r),start 显示源代码:list(l) 显示当前栈帧和运行行:frame(f) 设置显示 ...

  8. C++实现常用的文件操作

    ①输出到文本文件(txt) 1 #include<iostream> 2 #include<fstream> 3 using namespace std; 4 int main ...

  9. 记录--纯CSS实现骚气红丝带

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 在本文中,我们将探讨如何使用 CSS 以最少的代码创造出精美的 CSS 丝带形状,并最终实现下面这个效果: 下面我们使用html和css来 ...

  10. Chrome浏览器:The request client is not a secure context and the resource is in more-private address ...

    1.概述 新版的chrome浏览器会校验发起端的域名和访问资源的域名直接的关系,如果客户端发起域名比访问资源所在的域名更public(开放),会导致The request client is not ...