前言

相信对于刚学习JavaScript的新手来说,去理解JS中的事件循环原理以及异步执行过程比较困难,但是这是JS必须要会的基础知识,逃避不能解决问题,笔者曾经也被这个知识点困扰过,现根据以往的经验编写此文章,旨在帮助大家彻底搞懂它们以及自我巩固,话不多说,进入正题。

注意:本篇文章主要是基于浏览器环境,Node环境没有研究过暂不讨论

引言

我们先来小试牛刀,看看下面这段代码是怎么执行的,例1:

  setTimeout(() => {console.log('time')});new Promise((resolve, reject) => {console.log('p1');resolve();}).then(() => {console.log('res')});console.log(1);// 输出: p1 1 res time

怎么样?你想的输出结果和实际的输出结果是一样的吗?如果是一样的说明你对事件循环有一定的了解,但是你真的已经清楚的知道了事件循环的原理吗?让我们继续往下看。

为什么会有事件循环?

JS是单线程的

众所周知:JavaScript 是一门单线程语言,也就是说,同一个时间只能做一件事。这是因为 Javascript 这门脚 本语言诞生的使命所致——JavaScript 是为处理页面中用户的交互,以及操作 DOM 而诞生的。比如我们对 某个 DOM 元素进行添加和删除操作,不能同时进行。 应该先进行添加,之后再删除。

单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这样所导致的问题是: 如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉

为了解决这个问题,利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许JavaScript 脚本创建多个线程,但是子线程完全受主线程控制。于是,JS 中出现了同步任务异步任务

同步任务和异步任务

  • 同步任务:

同步任务都在主线程上执行,形成一个执行栈。在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;

  • 异步任务:

不进入主线程、而进入”任务队列”的任务,当主线程中的任务运行完了,才会从”任务队列”取出异步任务放入主线程执行。JS 的异步是通过回调函数实现的。异步任务相关回调函数添加到任务队列中(任务队列也称为消息队列)

注意:异步任务执行机制在这里描述的比较笼统,主要方便大家理解,具体细节在后面的“宏任务与微任务”中会详细介绍

JS的事件循环就是基于同步任务与异步任务来展开的,让我们继续往下看:

JS事件循环

事件循环是JavaScript实现异步的一种方法,也是JavaScript的执行机制

如图:

当一个脚本第一次执行的时候,js引擎会解析这段代码,并将其中的同步代码按照执行顺序加入执行栈中,然后从头开始执行。

遇到异步任务时不会一直等待事件的返回结果,而是将事件挂起(即交给其他线程处理,上图是指Web Worker),继续执行执行栈中的其他任务。

当异步事件返回结果时,js将异步事件callback函数放入队列中,被放入队列中的异步事件不会立即回调,等到当前执行栈中的任务都执行完成,处于闲置状态的主线程按照队列顺序将处于首位事件的callback函数放入执行栈中,执行该函数的同步代码,如果遇到了异步事件,同样也会将其回调函数放入事件队列中…

如此反复,就形成了一个循环,这也是被称为“事件循环(EventLoop)”的原因。

js事件循环的基本原理已经描述清楚,但是异步任务之间也有所不同:

任务队列实际上分为两个:宏任务队列和微任务队列。上图只表示了一个是为了便于大家理解事件循环,下面就是事件循环更细节的东西了

宏任务与微任务

上面讲到,js在执行异步任务时,回调函数会被放在js的任务队列中,实际上,回调函数的类别不同,执行的优先级也不同。

不同的优先级被分为两类,一类是宏任务(Micro task),一类是微任务(Macro task)。

回调函数是微任务时,会被放在微任务队列,回调函数是宏任务时,会被放在宏任务队列。

微任务的优先级高于宏任务,当主线程的任务执行完成时,会首先去执行微任务队列中首位的回调函数,当微任务队列中为空时,才回去执行宏任务队列中的回调函数。

常见的宏任务有哪些?

  • 包括整体代码 script
  • setTimeout()
  • setInterval()
  • setImmediate()(Node独有)
  • I/O
  • UI 交互事件(浏览器独有)
  • requestAnimationFrame() (浏览器独有)

常见的微任务有哪些?

  • Promise.then(); Promise.cath()
  • async/await
  • process.nextTick() (Node独有)
  • MutationObserver() (H5新增,监听DOM树变化)
  • Object.observe() (异步监视对象修改,已废弃)

注意:new Promise()属于同步任务,但是Promise.then(); Promise.cath()属于异步任务的微任务

执行过程总结(重点)

现在我们对事件循环有了深入了解了,但是它们的执行过程还不是很清晰,我们再把执行过程弄清楚了以后就能游刃有余了。

同步任务 —> 微任务 —> 宏任务...

  1. 先执行所有同步任务,碰到异步任务放到任务队列中
  2. 同步任务执行完毕,开始执行当前所有的异步任务
  3. 先执行任务队列里面所有的微任务,如果执行过程中又产生了微任务也会在本次执行过程中执行(即在下一个宏任务执行之前执行,可以看看案例1)
  4. 然后执行一个宏任务(从宏任务队列头部pop出一个宏任务进执行栈,该任务中的具体代码也如步骤1执行)
  5. 然后再执行所有的微任务(此时的微任务一般为步骤4中产生出的微任务)
  6. 再执行一个宏任务,再执行所有的微任务·······依次类推到执行结束。

3-6的这个循环称为事件循环Event Loop

案例挑战

学会了吗?让我们来做几个案例巩固一下吧

案例1:

const promise = new Promise((resolve, reject) => {resolve("10")}).then(res => {console.log("res1:", res)    //res1: hahahareturn 9}).then(res => {console.log("res2:", res)    //res2: 9return 8}).then(res => {console.log("res3:", res)    //res3: 8let promise2=new Promise((resolve,reject)=>{resolve("p2")}).then(res=>{console.log(res)setTimeout(function(){console.log("setTimeout2")},0)})})console.log('aaa')setTimeout(function(){console.log("setTimeout1")},0)const promise1 = new Promise((resolve, reject) => {console.log("p1")resolve(989)
}).then(res => {console.log(res)return 990
}).then(res=>{console.log(res)return 991
}).then(res=>{console.log(res)return 0
})/*输出结果:
aaa
p1
res1: 10
989
res2: 9
990
res3: 8
991
p2
setTimeout1
setTimeout2
*/

案例2:

console.log('1');// 定义注解 setTimeout_1 用于下文使用方便
setTimeout(function() {console.log('2');process.nextTick(function() {console.log('3');})new Promise(function(resolve) {console.log('4');resolve();}).then(function() {console.log('5')})
})
process.nextTick(function() {console.log('6');
})
new Promise(function(resolve) {console.log('7');resolve();
}).then(function() {console.log('8')
})// setTimeout_2
setTimeout(function() {console.log('9');process.nextTick(function() {console.log('10');})new Promise(function(resolve) {console.log('11');resolve();}).then(function() {console.log('12')})
})// 输出结果:  1 7 6 8 2 4 3 5 9 11 10 12

案例3:

console.log('1');    setTimeout(function() {console.log('2');process.nextTick(function() {console.log('3');})new Promise(function(resolve) {console.log('4');resolve();}).then(function() {console.log('5')})
})process.nextTick(function() {console.log('6');
})new Promise(function(resolve) {console.log('7');resolve();
}).then(function() {console.log('8')
})setTimeout(function() {console.log('9');process.nextTick(function() {console.log('10');})new Promise(function(resolve) {console.log('11');resolve();}).then(function() {console.log('12')})
})// 输出结果:1 7 6 8 2 4 3 5 9 11 10 12

JavaScript事件循环剖析相关推荐

  1. onpaste事件不生效_从实际开发中来看JavaScript事件循环的使用场景

    前言: 本文是介绍结合DOM事件流和JavaScript事件循环解决一个工作中的实际问题的过程,很多东西不只是面试的时候才会用得到 文中涉及到的代码demo地址:drag-and-eventloop ...

  2. 我理解的javascript事件循环(一)

    javascript事件循环分为2种:一种是浏览器端事件循环,一种是node端事件循环. 此文只是捋一捋我对浏览器端事件循环的理解. 前言 我们都知道 JavaScript 是一门单线程语言,这意味着 ...

  3. JAVA script 循环 图片_深入分析JavaScript 事件循环(Event Loop)

    事件循环(Event Loop),是每个JS开发者都会接触到的概念,但是刚接触时可能会存在各种疑惑. 众所周知,JS是单线程的,即同一时间只能运行一个任务.一般情况下这不会引发问题,但是如果我们有一个 ...

  4. dom更新到底在javascript事件循环的哪个阶段?「前端每日一题v22.11.17」

    dom更新到底在javascript事件循环的哪个阶段?「前端每日一题v22.11.17」 昨天写了一篇文章,是javascript的事件循环机制,然后在某乎上也发了,在发的时候看到了一个问题,dom ...

  5. JavaScript事件循环机制

    众所周知JS是一门单线程执行环境的语言,对于同步任务而言,同一时刻只能执行一个任务,后续的任务都要在当前执行的任务后面排队.这种模式在遇到一些执行时间较长的任务的时候就会出问题,会导致页面失去响应.所 ...

  6. 笔试题——JavaScript事件循环机制(event loop、macrotask、microtask)

    今天做了一道笔试题觉得很有意义分享给大家,题目如下: setTimeout(()=>{console.log('A'); },0); var obj={func:function () {set ...

  7. JavaScript事件循环探索

    一直对js的事件循环不是很清晰,最近看了JavaScript忍者秘籍的第13章后,有了一些感悟,特此总结一下,分享给大家. 单线程 众所周知,JavaScript是单线程执行模型,同一时刻只能执行一个 ...

  8. [译] 深入理解 JavaScript 事件循环(二)— task and microtask

    引言 microtask 这一名词是 JS 中比较新的概念,几乎所有人都是在学习 ES6 的 Promise 时才接触这一新概念,我也不例外.当我刚开始学习 Promise 的时候,对其中回调函数的执 ...

  9. JavaScript事件循环

    大厂面试题分享 面试题库 后端面试题库 (面试必备) 推荐:★★★★★ 地址:前端面试题库 一.异步执行原理 1. 单线程的JavaScript 我们知道,JavaScript是一种单线程语言,它主要 ...

最新文章

  1. tcp 测试工具_6款免费网络延迟测试工具
  2. Windows 10全新分支版本曝光!专门优化高配置PC
  3. thinkphp mysql 中文 问号_thinkphp分页中文参数乱码解决
  4. “现男友”来了!荣耀手机正式官宣
  5. Python+selenium自动化测试环境安装
  6. 10 Seconds Count Down
  7. php与sap系统,sap系统是什么意思
  8. 【扫盲系列】网络术语
  9. Q 语言 -- 赋值表达式
  10. mysql 查询重复数据并删除
  11. python货币转化为资本的前提_Python与《资本论》:生产资本中劳动力 A 与生产资料 Pm 的配比关系...
  12. IP地址的组成及简单分类
  13. 创建一个urdf机器人_ROS机器人Diego制作16-创建机器人的urdf模型描述文件
  14. DL中常用的三种K-Lipschitz技术
  15. S4D440Customcode adaption practice
  16. 服务器重启后启动php项目
  17. 两个有序表的合并(三种方法)
  18. 添加项目到debug调试
  19. 到Nexus私服的发包实践
  20. HDU 6595 Everything Is Generated In Equal Probability (期望dp,线性推导)

热门文章

  1. 外包程序员入职蚂蚁金服被质疑,网友评论人生污点,真就不拿外包当人了呗?
  2. android统计app流量的软件,流量控(手机流量统计)app
  3. Dubbo2.6.x—注册中心源码分析 dubbo-registry模块 (api and zookeeper)
  4. 茧数SCRM峰会获客案例:这家品牌商如何让98%的参会者,成为企业潜在客户?
  5. 深入理解OpenStack Neutron之---2 Neutron的网络实现模型---读书笔记
  6. 高效Android开发者必须知道的4个工具
  7. 嘉创房地产拟以介绍方式在港交所上市,期内营收、净利润规模可观
  8. CodeForces 1253 C. Sweets Eating DP
  9. CDH/CM:创建集群多节点,通过 VMware 安装 Centos 7 虚拟机 CentOS Linux release 7.6.1810
  10. 咆哮的计算机音乐乐谱大全,《咆哮双手简谱版》原创钢琴曲谱,EXO演唱 - 钢琴曲谱 - 器乐谱 - 词曲网...