前言
众所周知JavaScript是个单线程的语言,但是为了能更快更好的处理程序,JavaScript有一个基于事件循环的并发模型,事件循环负责执行代码、收集和处理事件以及执行队列中的子任务。这个模型也是JavaScript异于其他语言(c java等)处理并发任务之处。本篇也是我在工作学习中自己对JavaScript事件循环的理解,下面我们一起来了解下JavaScript中事件循环是如何执行的。
javascript中事件循环相关概念
在了解事件循环之前我们需要谈谈常见的语言的执行流程。
- 多线程:什么事多线程呢,最典型的语言是java,学过Java的小伙伴应该很容易理解,java中的Thread就是用来处理多线程的,及我们在java程序中可以同时运行多个线程。
- 单线程:不同于多线程,单线程工作的语言在同一时间段只能执行一个任务,就如同JavaScript在添加DOM的同时不能去删除DOM。但是如果仅仅如此拿我们在打开页面的同时去请求接口,那是不是要等接口返回之后才能做其他的事情,从实际情况来看肯定不是这样的,由此就可以引入一个异步的概念。
- 同步:可以看成一种流程执行方式,执行完A在执行B,这个符合我们正常的做事方式。
- 异步:同步变成我们都很好理解,A执行完执行B,异步与之相反执行A后直接执行B不用等待A执行完,A通过状体来通知调用A者,或者通过回掉函数来处理这个调用。
JavaScript这么语言就是单线程异步执行的,而事件循环就是为了处理异步这种执行方式我们需要一种方式来按照调用顺序执行。
在JavaScript中事件循环如何实现
先看图:
网络异常,图片无法展示
|
上图mdn中对事件循环的可视化描述,下图是我自己对执行流程的可视化描述。
网络异常,图片无法展示
|
从上面两张可视化图中我们可以看出整个事件循环是由以下几块组成:调用栈 任务队列 事件触发线程 WebAPi
- 调用栈:在JavaScript,js的函数调用形成了一个由若干帧组成的栈。
function A(c){ return 1+c } function B(b) { let a = 10 + b; return A(a); } function C(x) { let y = 3; return B(x * y); } console.log(C(7)); // 32
- 任务(消息)队列:一个 JavaScript 运行时包含了一个待处理消息的消息队列。每一个消息都关联着一个用以处理这个消息的回调函数。
- 事件触发线程: 浏览器渲染引擎提供的,它会维护一个 任务队列,我们也可以理解为这是一个任务调度器,他决定何时去将任务队列推到执行栈中,具体的实现比较复杂在此不做过多叙述。
- WEB API:浏览器一些产生异步任务的API。
//promise let p = new Promise(); p.then() //process.nextTick
- 事件循环流程:调用栈中中的任务都执行完后,栈内将被清空,此时主线程的一轮执行完毕,这个时候就会去任务队列中按照顺序读取一个任务放入到栈中执行。每次栈内被清空,都会去读取任务队列有没有任务,有就读取执行,一直循环读取-执行的操作,就形成了事件循环。
任务队列
- 同步任务:任务在主线程上同步执行,以队列的形式执行,前一个完成执行第二个。
- 异步任务:依赖于主线程执行的任务,但是不存在主线程的任务中,执行条件是当任务队列通知主线程执行某条任务时,此任务才能放入主线程中执行。
- 浏览器任务:在当代浏览器中同步的任务已经不能满足我们日益复杂的需求,因此浏览器在任务处理上引入了微任务的概念。具体执行如下:
1.宏任务在队列中,当调用栈中的函数执行完毕,事件循环系统会将下一个宏任务推入调用栈中执行,并且在执行过程中收集微任务。
2.当某次事件循环的宏任务执行完成之后,立马执行当前任务队列下微任务中的所有任务。微任务执行过程中将再次收集宏任务,并加入宏任务队列,以此循环往复的执行。
- 常见宏任务:setTimeout, setInterval, setImmediate,DOM解析渲染 ...# 实现代码
- 常见的微任务:promise process.nextTick
总结
js中事件循环是JavaScript引擎能高速处理页面操作的基石,如果没有这个能力,那我们就按照单线程的方式来执行页面上的操作,其结果无法想象,假设我们在一个网站上操作,你点击个按钮发送异步请求等到几秒之后页面才能再次操作,我想你永远不会想在此进入这个网站,因此我们如果想要真正的了解整个网站在浏览器上的工作流程第一步是去了解浏览器的事件循环机制。