说说 Event Loop
最近浏览了很多前端的面试题,发现绝大多数题目里都有问到 Event Loop。正好我收集整理了部分相关的信息,于是也来说说我理解的 Event Loop。
从单线程说起
为什么会有 Event Loop?这就要从 Javascript 的特点“单线程”开始说起了。
单线程是什么?意思就是在一个时间内程序只能做一件事。很多人都用过 Java 或者 C++ 之类的语言,肯定能体会到这些语言的多线程带来的很多便捷性。然而,Javascript 在设计之初的定位是用来处理用户交互以及操作 DOM,如果 Javascript 也设计成多线程,势必会带来很复杂的同步问题。
既是单线程又是异步
单线程的 JS 中,所有的任务都要排队,只有等前一个任务执行完毕才会执行后一个任务。
那所谓的异步又是怎么回事呢?
在单线程的 Javascript 中,涉及到大 IO 操作的任务,我们都可以为其注册回调函数:当程序执行到 IO 操作时,主线程将这个操作挂起,继续执行接下来的操作。等到 IO 操作完成之后,再将挂起的任务继续执行下去。
这个注册了回调函数的任务,我们可以叫它“异步任务”。
任务队列(EventQueue)
任务队列是一个队列,具有先进先出的特点。也就是说,先被加入任务队列的任务,会优先被主线程读取。
当一个异步任务完成之后,JS 就会触发某一指定事件(比如说 onload, onerror),则其所指向的函数(回调函数)就会被加入到任务队列中。
可以尝试理解以下代码。
const img = document.createElement('img')
img.src = 'https://image.hduzplus.xyz/image/1507523489652.jpg'
img.onload = function() {
console.log(1)
}
const timeoutFunc = function() {
console.log(3)
}
setTimeout(timeoutFunc, 0)
console.log(2)
// 执行结果:2 3 1
异步任务和同步任务
在 JS 中,任务分为两种,一种是上文提到的异步任务,另一种是同步任务。
同步任务意思就是在主线程上依此执行的任务。
异步任务就是进入任务队列的任务。只有等其执行完毕,任务队列通知主线程后,这个异步任务才会进入到主线程执行。
异步执行机制
- 所有的同步任务在主线程上依此执行;
- 异步任务执行完成后,相应事件的回调函数被加入到任务队列中;
- 主线程上的同步任务执行完毕后,将任务队列内的任务依此移入主线程执行;
- 重复以上 3 步。
// 伪代码
while(true) {
// 执行主线程操作
while(p = eventQueue.out()) {
// process p
}
}
Event Loop
主线程循环的从异步队列中读取事件,这个过程其实就是 Event Loop。
接下来可以看之前的代码。
主线程顺序执行,遇到异步任务则将其挂起,等待异步任务执行完成后再将其回调加入主线程。
所以先输出主线程中的'2'
图片 load 事件比 setTimeout 事件执行的慢,所以 setTimeout 的回调被先加入任务队列。所以输出'3'
最后输出'1'
扩展:异步任务是如何在单线程中执行的?
事实上,JS 的单线程指的是语言层面的单线程。在浏览器中我们执行某个 ajax 异步操作,这项异步操作是由浏览器完成的。我们注册的回调函数,实际上是向浏览器提交了这个回调函数。当异步操作完成之后,浏览器将该回调函数传入到执行上下文中的任务队列内。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于