说说 Event Loop

本贴最后更新于 2193 天前,其中的信息可能已经时移世改

说说 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 中,任务分为两种,一种是上文提到的异步任务,另一种是同步任务。
同步任务意思就是在主线程上依此执行的任务。
异步任务就是进入任务队列的任务。只有等其执行完毕,任务队列通知主线程后,这个异步任务才会进入到主线程执行。

异步执行机制

  1. 所有的同步任务在主线程上依此执行;
  2. 异步任务执行完成后,相应事件的回调函数被加入到任务队列中;
  3. 主线程上的同步任务执行完毕后,将任务队列内的任务依此移入主线程执行;
  4. 重复以上 3 步。
// 伪代码
while(true) {
  // 执行主线程操作
  while(p = eventQueue.out()) {
    // process p
  }
}

Event Loop

主线程循环的从异步队列中读取事件,这个过程其实就是 Event Loop。

接下来可以看之前的代码。
主线程顺序执行,遇到异步任务则将其挂起,等待异步任务执行完成后再将其回调加入主线程。
所以先输出主线程中的'2'
图片 load 事件比 setTimeout 事件执行的慢,所以 setTimeout 的回调被先加入任务队列。所以输出'3'
最后输出'1'

扩展:异步任务是如何在单线程中执行的?

事实上,JS 的单线程指的是语言层面的单线程。在浏览器中我们执行某个 ajax 异步操作,这项异步操作是由浏览器完成的。我们注册的回调函数,实际上是向浏览器提交了这个回调函数。当异步操作完成之后,浏览器将该回调函数传入到执行上下文中的任务队列内。

  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品:SymSoloVditor思源笔记

    1090 引用 • 3467 回帖 • 297 关注
  • 研究
    12 引用 • 34 回帖
  • 前端

    前端技术一般分为前端设计和前端开发,前端设计可以理解为网站的视觉设计,前端开发则是网站的前台代码实现,包括 HTML、CSS 以及 JavaScript 等。

    247 引用 • 1347 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • 与 Android 的 Handler 机制有点像

  • 其他回帖
  • salamander 1

    道理不难,异步任务的执行应该是开了一个进程去做的,执行完后把 callback 放入任务队列,js 执行栈空闲去看看任务队列有没有 callback,有的话执行这个 callback
    Nodejs 也是这个原理,天生的异步非阻塞,但这个模式不适合 CPU 密集型的任务,因为执行栈一旦阻塞,别的什么的就全阻塞了

  • someone

    哈哈,要是没有一峰大哥,我肯定没有现在这点实力~

    1 回复
  • cbam

    牛逼啊

  • 查看全部回帖