前言
Event Loop
即事件循环,是指浏览器或Node
的一种解决javaScript
单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。
什么是 Event Loop?
Event Loop 是一个很重要的概念,指的是计算机系统的一种运行机制。
想要理解Event Loop,就要从程序的运行模式讲起。运行以后的程序叫做"进程"(process),一般情况下,一个进程一次只能执行一个任务。
如果有很多任务需要执行,不外乎三种解决方法。
(1)排队。因为一个进程一次只能执行一个任务,只好等前面的任务执行完了,再执行后面的任务。
(2)新建进程。使用fork命令,为每个任务新建一个进程。
(3)新建线程。因为进程太耗费资源,所以如今的程序往往允许一个进程包含多个线程,由线程去完成任务。
以JavaScript语言为例,它是一种单线程语言,所有任务都在一个线程上完成,即采用上面的第一种方法。一旦遇到大量任务或者遇到一个耗时的任务,网页就会出现"假死",因为JavaScript停不下来,也就无法响应用户的行为。
你也许会问,JavaScript为什么是单线程,难道不能实现为多线程吗?
这跟历史有关系。JavaScript从诞生起就是单线程。原因大概是不想让浏览器变得太复杂,因为多线程需要共享资源、且有可能修改彼此的运行结果,对于一种网页脚本语言来说,这就太复杂了。后来就约定俗成,JavaScript为一种单线程语言。(Worker API可以实现多线程,但是JavaScript本身始终是单线程的。)
Event Loop 是什么?
Wikipedia中对Event Loop的描述为:
在计算机科学中,Event Loop是指一个用于等待和发送消息和事件的程序结构(In computer science, the
event loop is a programming construct or design pattern that waits for and dispatches events
or messages in a program. )
简单来说,就是在除主线程之外设置一个用于协调主线程中和其他线程的一个“辅助线程”的结构
Event Loop的具体内容
在完整讲述Event Loop之前,需要先讲解一下Javascript的运行机制;在javascript中,代码(程序)的执行一般遵循一下的机制:
- 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)
- 主线程之外还存在“任务队列/调用队列”(task queue),用于存放等待执行的任务
- 每当“执行栈”中的同步任务执行完成后,主线程将会将“任务队列”中的第一个任务加入“执行栈”
执行栈
当我们调用一个方法的时候,js会生成一个与这个方法相对应的执行环境,也叫执行上下文,这个执行环境存在着这个方法的私有作用域、参数、this对象等等。因为js是单线程的,同一时间只能执行一个方法,所以当一系列的方法被依次调用的时候,js会先解析这些方法,把其中的同步任务按照执行顺序排队到一个地方,这个地方叫做执行栈。
任务队列
当我们发出一个ajax请求,他并不会立刻返回结果,为了防止浏览器出现假死或者空白,主线程会把这个异步任务挂起(pending),继续执行执行栈中的其他任务,等异步任务返回结果后,js会将这个异步任务按照执行顺序,加入到与执行栈不同的另一个队列,也就是事件队列。
以上的事件循环过程只是一个宏观的表述,实际上异步任务之间也不相同,执行优先级也有区别。不同的异步任务被分为两类:宏任务(macro task)和微任务(micro task)。我们将经常遇到的异步任务进行分类如下:
在javascript中,任务被分为两种,一种宏任务(MacroTask
)也叫Task
,一种叫微任务(MicroTask
)。
其中,宏任务指的是:
-
script
全部代码 setTimeout
setInterval
-
setImmediate
(浏览器暂时不支持,只有IE10支持,具体可见MDN) I/O
UI Rendering
微任务指:
Process.nextTick(Node独有)
Promise
Object.observe(废弃)
-
MutationObserver
(具体使用方式查看这里)
按照前面的介绍,异步任务会按照执行完成的顺序添加到任务队列中;事实上,根据异步任务的不同类型,这个任务最终会分别添加到宏任务队列和微任务队列中去
当执行栈中的任务清空,主线程会先检查微任务队列中是否有任务,如果有,就将微任务队列中的任务依次执行,直到微任务队列为空,之后再检查宏任务队列中是否有任务,如果有,则每次取出第一个宏任务加入到执行栈中,之后再清空执行栈,检查微任务,以此循环... ...
简单来说,就是微任务队列中的所有任务的优先级都高于宏任务队列中的任务