V8引擎详解(八)——消息队列

4,946 阅读5分钟

前言

本文是V8引擎详解系列的第八篇,重点内容是关于V8引擎的消息队列,会通过单线程的特点入手来学习,逐步其消息队列的运行机制。
文末会有已经完成的系列文章的链接,本系列文章还在不断更新欢迎持续关注。

单线程

为什么是单线程

由于javascript最初作为浏览器脚本语言,主要用来与用户互动、操作dom等,如果有多个线程同时操作一个DOM的情况,会导致非常难以处理,所以javascript只能设计成单线程。

不过现代计算机基本都是多核CPU的,纯粹的单线程会导致一些性能得不到释放,所以新的 HTML5 标准中提出了 web worker概念,允许用户额外开启线程,不过 worker 线程是完全受主线程控制(大部分情况处理一些计算逻辑),且没有操作DOM的权限,本质上javascript还是单线程。

异步任务

javascript只有一个主线程用来执行任务,但是同一时间只能执行一个任务也就是函数,普通的函数会形成一个任务队列排队执行,但是有些任务会非常耗时且不可控(网络请求、事件监听)等,如果让这些任务也和普通任务一样排队执行,那么执行效率低不说还会导致页面的卡死。
于是就有了异步任务,而 V8 引擎 通过消息队列事件循环 系统让异步任务执行且不用排队等待执行完毕。

消息队列

消息队列是 V8引擎 除了主线程任务外,额外维护的一个队列,主要存放要执行的任务(函数)。完全符合队列 先进先出 的特点,从队列的头部取出任务,从队列的尾部添加任务。

浏览器本身需要异步的场景非常多,而每一种异步操作的机制也各不相同, 消息队列可以和多种异步场景产生交互。

  • 输入事件相关的异步交互(比如onClick)由引擎的 DOM Binding 模块处理,相应的事件触发时,会将对应的回调函数添加到消息队列中。
  • ajax请求相关的异步交互 由引擎的network模块处理,在网络请求完成返回之后,会将对应的回调函数添加到消息队列中。
  • 定时器相关的异步交互 会引擎的 timer 模块处理,当时间到达的时候,会将回调函数添加到任务队列中。(定时器调度策略比较复杂,会有专门的调度策略在合适的时间添加对应的回调任务)

以及一些其他的模块会将异步操作放置到消息队列中,在引擎主线程的任务都执行完成后再执行消息队列中被推送的任务。
具体如下图:

(图片来源:《Help, I'm stuck in an event-loop》

那么消息队列会在什么时候执行呢?
每次执行栈中的代码就是一个宏任务(task),而消息队列中的任务会按顺序放到下一次的宏任务(task)中,每个宏任务(task)在执行时,V8 都会重新创建栈,然后随着宏任务(task)中函数调用,栈也随之变化,最终,当该宏任务(task)执行结束时,整个栈又会被清空,接着主线程继续执行下一个宏任务(task)

而浏览器会在一个 宏任务(task) 执行结束后,在下一个 task 执行开始前,对页面进行重新渲染如图:

由于主线程执行消息队列中宏任务的时间颗粒度太粗了(主要中间有一次渲染过程),无法胜任一些对精度和实时性要求较高的场景,所以又引入了promise 机制也就是微任务如图:

(本篇的重点不在于宏任务和微任务所以简单带过,但是我想表达的是,现在前端面试大概率都会问到宏任务和微任务,很多人都只会回答到宏任务和微任务的执行顺序,但是如果你回答出中间还有一个页面渲染的过程,才算真正了解)

事件循环

如果你能理解上文的消息队列机制,那么事件循环就很好理解了,本质上就是

  • 主线程运行产生了执行栈,在调用执行栈的过程中调用了一些异步函数。
  • 当满足异步函数的触发条件时,会将对应的回调函数推送到消息队列中。
  • 当主栈中的代码执行完毕时,会触发一次页面渲染,然后创建新的主栈。
  • 将消息队列中的回调函数推送到主栈。然后顺序执行主栈的任务。
  • 反复循环执行上述过程就是时间循环。

总结

本文主要了解了关于V8引擎的消息队列的运行机制,也简单的了解了一下为什么js使用单线程机制,以及简单说了一下宏任务和微任务(网上相关的好文章非常多,所以本文就不重复赘述,只是简单的点了一下容易被忽略的点)。如果有什么错误,请在评论中和作者一起讨论,如果您觉得本文对您有帮助请帮忙点个赞,感激不尽。

参考文章

vimeo.com/96425312 time.geekbang.org/column/arti…

系列文章

V8引擎详解(一)——概述
V8引擎详解(二)——AST
V8引擎详解(三)——从字节码看V8的演变
V8引擎详解(四)——字节码是如何执行的
V8引擎详解(五)——内联缓存
V8引擎详解(六)——内存结构
V8引擎详解(七)——垃圾回收机制
V8引擎详解(八)——消息队列
V8引擎详解(九)——协程&生成器函数