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(); 会得到 ...
随机推荐
- RIPEMD算法:多功能哈希算法的瑰宝
一.RIPEMD算法的起源与历程 RIPEMD(RACE Integrity Primitives Evaluation Message Digest)算法是由欧洲研究项目RACE发起,由Hans D ...
- 基于STM32F407MAC与DP83848实现以太网通讯一(STM32以太网(ETH)外设)
STM32F4xx 可以通过以太网按照 IEEE 802.3-2002 标准发送和接收数据.支持与外部物理层 (PHY) 相连的两个工业标准接口:默认情况下使用的介质独立接口 (MII)(在 IEEE ...
- Vue.prototype.$app = this vscode ctrl 点击 不跳转
Vue.prototype.$app = this vscode ctrl 点击 不跳转 解决思路 src/types/vue.d.ts 里面写,具体还没解决,找到一篇 在js里面的 我这种直接将当前 ...
- Android Studio 下载jcenter依赖失败问题及解决
优先国内的镜像下载即可 我用的华为和阿里云的国内镜像 google() maven { url 'https://jitpack.io' } maven { url 'http://maven.ali ...
- 01_Mac安装Homebrew
目录 1 官方 1.1 前提 1.2 安装 2 其他安装方法 2.1 安装homebrew-core 2.2 安装homebrew-cask 3 源 3.1 查看源 3.2 设置源 3.3 设置bot ...
- TP6框架--EasyAdmin学习笔记:excel的数据,图片处理
这是我写的学习EasyAdmin的第七章,这一章我给大家分享下如何处理excel中的数据,图片 原理就是使用easyadmin中封装好的phpexcel,来进行数据的导入,view层我们需要一个用了导 ...
- demo介绍:uni-app统合--各类大转盘demo介绍
这里给大家介绍下我做的一个uni-app的demo:抽奖系统 废话不多说直接上效果图: 线上案例: 首页: 大转盘抽奖页面: 轮播机抽奖页面: 九宫格抽奖页面: 翻牌抽奖页面: 奖品列表页面: 抽奖的 ...
- Vue3项目-生成Cron表达式组件
最近做的一个vue3项目过程中,需要用到cron表达式功能,而对于普通业务人员,他们是不懂cron表达式规则的,所以需要做一个可手动配置生成cron表达式的功能.从网上查找了一些相关资料,然后结合vu ...
- MyBatis中的association与collection应用
MyBatis中的association与collection应用 在使用 MyBatis进行数据库操作时,经常会遇到需要处理对象之间的关联关系和集合映射的情况.为了更好地实现对象关系映射,MyBat ...
- KingbaseES 原生XML系列二 -- XML数据操作函数
KingbaseES 原生XML系列二--XML数据操作函数(DELETEXML,APPENDCHILDXML,INSERTCHILDXML,INSERTCHILDXMLAFTER,INSERTCHI ...