setTimeout(fn, 0) // it works - JavaScript 事件循环 动画演示
在前端代码中很经常看到使用 setTimeout(fn, 0),如下面代码所示,乍一看很多余,但是移除了可能会出现一些奇奇怪怪的问题。要解释这个就需要理解 事件循环(Event Loop),下面会通过一些例子和动画来辅助理解事件循环
setTimeout(() => {
// 调用一些方法
}, 0)
为什么使用事件循环
JS 是单线程的(浏览器和 Node则是多线程的),为了避免 渲染主线程 阻塞,需要异步,事件循环 是异步的实现方式
浏览器在一个渲染主线程中运行一个页面中的所有 JavaScript 脚本,以及呈现布局,回流,和垃圾回收。为了避免 同步 的执行方式导致渲染主线程阻塞,使得页面卡死,所以浏览器采用异步的方式:渲染主线程将任务交给其他线程去处理,自身 立即结束 任务的执行,转而执行后续代码,当其他线程完成时,将事先传递的回调函数包装成任务,加入到对应的消息队列的末尾排队,等待渲染主线程调度执行
流程:
- 渲染主线程执行全局 JS,需要异步的任务放到对应的队列,如果是
setTimeout则会有线程计时,到了指定时间会将任务放入延时队列(并非立即执行) - 渲染主线程为空时,按队列的优先级依次选择队列(最先执行微队列的任务),依次按顺序执行各个队列的任务
任务没有优先级,而消息队列有优先级,不同任务分属于不同队列:参考 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 事件循环 动画演示的更多相关文章
- JavaScript下的setTimeout(fn,0)意味着什么?
近期在研究异步编程的我对于setTimeout之类的东西异常敏感.在SegmentFault上看到了一个问题<关于SetTimeout时间设为0时>:提问者读了一篇文章,原文解释setTi ...
- 【 js 基础 】【 源码学习 】 setTimeout(fn, 0) 的作用
在 zepto 源码中,$.fn 对象 有个 ready 函数,其中有这样一句 setTimeout(fn,0); $.fn = { ready: function(callback){ // don ...
- setTimeout(fn, 0) 的作用
在 zepto 源码中,$.fn 对象 有个 ready 函数,其中有这样一句 setTimeout(fn,0); 1 $.fn = { 2 ready: function(callback){ 3 ...
- setTimeout(fn, 0)引发的JavaScipt线程的思考
起因 周五改一个checkbox的display属性被错误地设置为none的bug. 经debug发现, 有两个地方修改了display属性: 1) checkbox的controller; 2) c ...
- c#封装DBHelper类 c# 图片加水印 (摘)C#生成随机数的三种方法 使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象 c# 制作正方形图片 JavaScript 事件循环及异步原理(完全指北)
c#封装DBHelper类 public enum EffentNextType { /// <summary> /// 对其他语句无任何影响 /// </summary> ...
- 关于setTimeout(fn,0)
JS是单线程引擎:它把任务放到队列中,不会同步去执行,必须在完成一个任务后才开始另外一个任务. 浏览器的内核是多线程的,它们在内核制控下相互配合以保持同步,一个浏览器至少实现三个常驻线程:javasc ...
- setTimeout(fn,0)的作用分析
众所周知,大家对setTimeout的用法肯定都比较熟悉了,但是不是还是会经常忘记使用呢,例如博主阿里面试时就忘了,见阿里前端面试. 今天跟大家讨论一下setTimeout(fn,0)的用法,相信很多 ...
- 一篇文章图文并茂地带你轻松学完 JavaScript 事件循环机制(event loop)
JavaScript 事件循环机制 (event loop) 本篇文章已经默认你有了基础的 ES6 和 javascript语法 知识. 本篇文章比较细致,如果已经对同步异步,单线程等概念比较熟悉的读 ...
- JavaScript 事件循环
JavaScript 事件循环 事件循环 任务队列 async/await 又是如何处理的呢 ? 定时器问题 阻塞还是非阻塞 实际应用案例 拆分 CPU 过载任务 进度指示 在事件之后做一些事情 事件 ...
- setTimeout(fn,0)
我们都知道setTimeout是一个延迟执行的函数 console.log(); setTimeout(function(){console.log();},); console.log(); 会得到 ...
随机推荐
- 19 SWERC 2022-2023 - Online Mirror (Unrated, ICPC Rules, Teams Preferred)L. Controllers(数学公式+瞎搞)
L. Controllers 思路: #include <bits/stdc++.h> #define int long long #define rep(i, a, b) for(int ...
- linux vs code extension C# `GLIBC_2.27' not found
settings中omnisharp:useModernNet改为true reboot虚机
- manjaro蓝牙链接不上或者连接上了没有声音
查看这些软件宝有没有安装上:sudo pacman -S bluez bluez-utils pulseaudio-bluetooth pavucontrol pulseaudio-alsa puls ...
- 【机器学习】统计学习方法读书笔记-EM算法(期望最大化算法)
Expectation Maximization,EM算法是带有隐变量的概率模型参数的极大似然估计(MLE为给定参数,观测数据出现/生成的可能性). 如下为<统计机器学习>中对应EM算法的 ...
- FreeRTOS教程4 消息队列
1.准备材料 正点原子stm32f407探索者开发板V2.4 STM32CubeMX软件(Version 6.10.0) Keil µVision5 IDE(MDK-Arm) 野火DAP仿真器 XCO ...
- 微型MPU6050模块及串口/BLE透传的实物展示
前言 随着时间的积累,项目的沉淀.把很多做过的产品标准化掉,形成可以立即拿出来使用的产品.是非常重要的一个环节.最近,把基于MPU6050的模块微型化并使用串口透传这个产品梳理了一下.形成了标准化 ...
- 生成文件名为系统时间的C源码实例
一 最近遇到了一个需要根据时间记录文件名的.先写一个实例来实战: #include<stdlib.h> #include<time.h> #include<stdio.h ...
- ESP8266 SPI 开发之软件驱动代码分析
一 基本概述 esp8266的SPI代码流程非常的清晰,主要有三部分构成: spi_init 配置 spi_trans 配置 data_transfer 配置这三块组成. 在这里,笔者就针对spi的这 ...
- day03-应用线程01
JavaGUI-坦克大战03 7.线程的应用01 7.1坦克子弹发射思路 在坦克大战2.0基础上添加如下功能:当玩家按下 j 键,就发射一颗子弹. 思路: 当发射一颗子弹后,就等于启动了一个线程 He ...
- LinuxDNS分析从入门到放弃(记一次有趣的dns问题排查记录,ping 源码分析,getaddrinfo源码分析)
PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 环境说明 ubuntu 18.04 前言 我们这里有一块 ...