Event Loop - 事件队列
Event Loop
定义:
event - 事件 loop - 循环,既然叫事件循环,那么循环的点在哪?
循环的是一个又一个的任务队列,这些任务队列由宏任务和微任务构成
两条原则
- 一次处理一个任务
- 一个任务开始后直到完成,不会被其他任务中断
事件处理之间的关系
一次事件处理中,最多处理一个宏任务,但是会处理所有的微任务,任务开始后,会将内部所有的宏观函数加到宏观队列等待,会将所有的围观函数加到微观队列等待,当前宏任务处理完毕后,开始逐个处理微任务,当微任务执行处理完成,会检查是否需要更新ui,如果是则重新渲染ui。之后再次检查宏观任务队列中的下一个宏观任务,取出来并且执行
执行规则

宏任务包含:
- 整体script(也叫js主进程)
- settimeout
- setinterval
- requestAnimationFrame
微任务包含:
- Promise.then catch finally
- Generator 函数
- async/await
- MutationObserver
异步任务都有哪些
- 回调函数
- Promise(注意new promise里面的属于同步任务)
- async
- Generator
- 事件监听
- 发布/订阅
- 计时器
- requestAnimationFrame
- MutationObserver
题外话
浏览器的渲染,不同浏览器处理是不同的,但是大部分浏览器选择一般是一秒钟60帧率(也就是60fps),这意味着浏览器会在16ms左右渲染一帧,所以我们单个任务和该任务的所有附属微任务都应该在16ms内完成,以达到显示平滑流畅。
怎么证明js事件队列存在,就拿简单的setTimeout来说
console.time("settimeout");
setTimeout(() => {
console.log('settimeout执行')
console.timeEnd("settimeout");
}, 1000);
for (let a = 0; a < 50000; a++) {
console.log(a)
}
time会输出多少呢?这种事件是怎么触发的?
代码是在一秒钟后才加入事件队列,之后等待执行。怎么证明,很简单我们在拿出一个定时器
console.time("settimeout");
console.time("settimeout2");
setTimeout(() => {
console.log('settimeout执行')
console.timeEnd("settimeout");
}, 1000);
setTimeout(() => {
console.log('settimeout2执行')
console.timeEnd("settimeout2");
}, 1);
for (let a = 0; a < 50000; a++) {
console.log(a)
}
明明后声明的定时器2,却先执行了,不知道有没有发现,定时器1和定时器2执行时间,很接近!!!这很重要,为什么会这样,这就是今天所说的事件队列,当1毫秒时候,定时器2被加入了事件队列,当一秒钟时候定时器1被加入事件队列,然后for执行完后,队列中下一个任务为定时器2,但是他只有一个console故而很快,所以拿出了定时器进行执行。再来看个额外例子,来巩固下:
console.time("settimeout");
console.time("settimeout2");
setTimeout(() => {
console.log('settimeout执行')
console.timeEnd("settimeout");
}, 1000);
for (let a = 0; a < 50000; a++) {
console.log(a)
}
setTimeout(() => {
console.log('settimeout2执行')
console.timeEnd("settimeout2");
}, 1);
枯燥无味的定义基本就这样,我们来从实践来做分析
从练习题来说
console.log(1)
setTimeout(function () {
console.log(2)
setTimeout(function () {
console.log(3)
})
})
setTimeout(function () {
console.log(4)
})
console.log(5)
第一次运行之后

这时当前主进程队列已经结束,开始检测微任务队列是否还有未完成的任务,发现微任务队列已经空了所以,当前宏任务队列结束,开始下一组宏任务

settimeout2任务完结,检查当前微任务队列为空,开始下一组宏任务

所以最终答案为:
1,5,2,4,3
- 注意:setTimeout是以其触发事件为写入队列时间。如果这么说不理解的话 可以将上面代码改为如下:
console.log(1)
setTimeout(function () {
console.log(2)
setTimeout(function () {
console.log(3)
})
})
setTimeout(function () {
console.log(4)
})
console.log(5)
这样就会输出1,5,2,3,4
tips: setTimeout写的1000不等于他就是在上一次事件结束后的1000ms,而是以他声明开始就进行计时的。不过这不是本篇文章的核心,我们不深究他的逻辑,继续看第二个例子
console.log(1)
setTimeout(() => {
console.log('2')
Promise.resolve(4).then((res) => console.log(res))
}, 0);
setTimeout(() => {
console.log('3')
}, 0);

主进程执行完毕,检查微任务队列为空,当前宏任务结束,开启下一组宏任务

settimeout宏任务执行完毕,检查宏任务队列,拿出settimeout3的宏任务,将它拿出来执行。这个比较简单咱们就不画图了
所以本题答案为1,2,4,3
let promise = new Promise(function(resolve, reject) {
console.log('1');
resolve();
});
promise.then(function() {
console.log('2');
});
console.log(3)

检查宏任务队列发现为空,所以本次代码结束
答案为:1,3,2
console.log(1);
setTimeout(function(){
console.log(2);
}, 0);
Promise.resolve().then(function(){
console.log(3);
}).then(function(){
console.log(4);
});

当前宏任务已经结束,查看宏任务队列中发现还有settimeout没有执行,将它取出来执行,
输出2.
所以本题答案为:1,3,4,2
setTimeout(()=>{
console.log('1');
},0);
var obj={
func:function () {
setTimeout(function () {
console.log('2')
},0);
return new Promise(function (resolve) {
console.log('3');
resolve();
})
}
};
obj.func().then(function () {
console.log('4')
});
console.log('5');

主线进程及其微观进程执行完毕,会拿出下一组settimeout1执行,执行后会进行检测微观队列,如果没有则会继续往下取出settimeout2执行。至此程序结束。
所以本题答案为:3,5,4,1,2
console.log(1)
const p = new Promise(function(resolve, reject) {
console.log(2)
resolve()
}).then(() => {
console.log(3)
throw(new Error('错误'))
}).catch(() => {
console.log(4)
})
console.log(5)
setTimeout(() => {
console.log(6)
}, 0);
console.log(7)
p.then(() => {
console.log(8)
throw(new Error('错误2'))
})

至此,当前宏任务结束。检查宏任务队列。取出下一组宏任务,settimeout6,并执行
所以答案为:1,2,5,7,3,4,8,Error,6
最后留两道题给大家做学习用。
如果不能一眼看出。可以像我一样画一个图。进行梳理。本文中为了代码整齐,settimeout都是直接简化为尽快执行。其实settimeout应该是在到达他声明的时间时候,才进入宏观队列排队的。
根据以下规则,你将不会在遇到事件队列的问题
- 一次处理一个任务
- 宏任务+当前所有微任务为一组
- 同步直接执行、异步会加入事件队列
- 所有异步的加入队列时间均以他们触发时间为准
console.log(1)
const p = new Promise(function(resolve, reject) {
console.log(2)
setTimeout(() => {
console.log(9)
}, 0);
resolve()
}).then(() => {
console.log(3)
throw(new Error('错误'))
}).catch(() => {
console.log(4)
})
console.log(5)
setTimeout(() => {
console.log(6)
}, 0);
console.log(7)
p.then(() => {
console.log(8)
throw(new Error('错误2'))
})
console.log(1)
const p = new Promise(function(resolve, reject) {
console.log(2)
setTimeout(() => {
console.log(9)
}, 0);
resolve()
}).then(() => {
console.log(3)
throw(new Error('错误'))
}).catch(() => {
console.log(4)
})
console.log(5)
setTimeout(() => {
console.log(6)
}, 0);
console.log(7)
p.then(() => {
console.log(8)
throw(new Error('错误2'))
})
requestAnimationFrame(function() {
console.log(10)
})
宏观微观
宏观微观不是嵌套!!!!!即使代码嵌套了在队列中也不是嵌套的!!!!
怎么利用这些,在代码中优化自己的代码,举个例子来说
for (let i = 0 ; i<50000;i++) {
const div = document.createElement('div')
div.innerText = i
document.body.appendChild(div)
}
function slice(startSplitNumber, total, sliceNumber, cb) {
const oneNumber = total/sliceNumber
const start = startSplitNumber * oneNumber
const end = (startSplitNumber + 1) * oneNumber
if (start >= total) return
setTimeout(() => {
for(let i = start; i < end;i++) {
cb(i)
}
slice(startSplitNumber + 1, total, sliceNumber, cb)
}, 0)
}
slice(0, 50000, 5000, function (current) {
const div = document.createElement('div')
div.innerText = current
document.body.appendChild(div)
})
Event Loop - 事件队列的更多相关文章
- js 在浏览器中的event loop事件队列
目录 前言 认识一个栈两个队列 执行过程 异步任务怎么分配 简单例子 难一点的例子 前言 以下内容是js在浏览器中的事件队列执行,与在nodejs中有所区别,请注意. 都说js是单线程的,不过它本身其 ...
- javascript运行模式:并发模型 与Event Loop
看了阮一峰老师的JavaScript 运行机制详解:再谈Event Loop和[朴灵评注]的文章,查阅网上相关资料,把自己对javascript运行模式和EVENT loop的理解整理下,不一定对,日 ...
- [译]Node.js - Event Loop
介绍 在读这篇博客之前,我强列建议先阅读我的前两篇文章: Getting Started With Node.js Node.js - Modules 在这篇文章中,我们将学习 Node.js 中的事 ...
- Node.js 事件循环(Event Loop)介绍
Node.js 事件循环(Event Loop)介绍 JavaScript是一种单线程运行但又绝不会阻塞的语言,其实现非阻塞的关键是“事件循环”和“回调机制”.Node.js在JavaScript的基 ...
- JavaScript 运行机制详解:再谈Event Loop
原文地址:http://www.ruanyifeng.com/blog/2014/10/event-loop.html 一年前,我写了一篇<什么是 Event Loop?>,谈了我对Eve ...
- 数据密集型 和 cpu密集型 event loop
Node.js在官网上是这样定义的:“一个搭建在Chrome JavaScript运行时上的平台,用于构建高速.可伸缩的网络程序.Node.js采用的事件驱动.非阻塞I/O模型使它既轻量又高效,是构建 ...
- JavaScript 运行机制详解:深入理解Event Loop
Philip Roberts的演讲<Help, I'm stuck in an event-loop>,详细.完整.正确地描述JavaScript引擎的内部运行机制. 一.为什么JavaS ...
- 对Node.JS的事件轮询(Event Loop)的理解
title: Node.JS的事件轮询(event loop)的理解 categories: 理解 tags: Node JS 机制 当我们知道I/O操作和创建新线程的开销是巨大的! 网站延迟的开销 ...
- javascript运行机制详解: 再谈Event Loop(转)
作者: 阮一峰 日期: 2014年10月 8日 一年前,我写了一篇<什么是 Event Loop?>,谈了我对Event Loop的理解. 上个月,我偶然看到了Philip Roberts ...
随机推荐
- C#中的release和debug模式
以下内容来源:https://www.cnblogs.com/rgjycs/p/9254332.html 在程序调试时的debug和release 网上有如下的描述:Debug 通常称为调试版本,它包 ...
- 01_cifsd 高性能网络共享服务
01_cifsd 高性能网络共享服务 1.简介 cifsd 是一款高性能I/O网络文件共享服务, 通过一种与kernel直接交互的方式实现, github简介:https://github.com/n ...
- 在springmvc.xml中定义全局的异常处理
在Controller类的内部方法上使用@ExceptionHandler,则此类的方法抛出未处理的异常时,回到此方法上处理. @ExceptionHandler可以指定异常的类型,会自动进行匹配 如 ...
- MySQL主主数据同步
环境 操作系统版本:CentOS 6.5 64位MySQL版本:mysql5.6节点1IP:192.168.0.235 主机名:taojiang1-mysql-01节点2IP:192.168.0.23 ...
- 使用 c++ 模板显示实例化解决模板函数声明与实现分离的问题
问题背景 开始正文之前,做一些背景铺垫,方便读者了解我的工程需求.我的项目是一个客户端消息分发中心,在连接上消息后台后,后台会不定时的给我推送一些消息,我再将它们转发给本机的其它桌面产品去做显示.后台 ...
- 白话科普系列——最好用的浏览器 Chrome,你用了么?
市面上的浏览器多种多样,而浏览器的王者--IE 浏览器,它在 2002 年市场份额高达 95.4%.直到后续 Firefox,Safari,Chrome 相继问世才动摇了 IE 浏览器的地位,其中 C ...
- [MIT6.006] 13. Breadth-First Search (BFS) 广度优先搜索
一.图 在正式进入广度优先搜索的学习前,先了解下图: 图分为有向图和无向图,由点vertices和边edges构成.图有很多应用,例如:网页爬取,社交网络,网络传播,垃圾回收,模型检查,数学推断检查和 ...
- Python_微信开发
<!-- 发消息功能 --> 0.微信开发的2个库 pip install werobot pip install 1.新建项目 2.项目下新建 robot 的app 3.写robot.p ...
- Elementary OS 使用fcitx安装搜狗词库、搜狗输入法(Linux通用)
刚开始接触Linux的小伙伴可能比较懵逼,我要使用ibus输入法还是fcitx(小企鹅)输入法,其实这两种都不能说是输入法,Linux中输入法的使用是依赖于输入法框架的,其中搜狗输入法和百度输入法都是 ...
- RedisUtils工具类
package test.util; import org.springframework.beans.factory.annotation.Autowired; import org.springf ...