图解 Google V8 # 17:消息队列:V8是怎么实现回调函数的?

Posted 凯小默

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图解 Google V8 # 17:消息队列:V8是怎么实现回调函数的?相关的知识,希望对你有一定的参考价值。

说明

图解 Google V8 学习笔记

什么是回调函数?

只有当某个函数被作为参数,传递给另外一个函数,或者传递给宿主环境,然后该函数在函数内部或者在宿主环境中被调用,才称为回调函数

回调函数有两种不同的形式:

  • 同步回调:函数在执行函数内部被执行的;
  • 异步回调:函数在执行函数外部被执行的。

UI 线程的宏观架构

注意:在浏览器的页面里面是UI线程,node中就是它的主线程。

什么是 UI 线程?

所谓 UI 线程,是指运行窗口的线程,当你运行一个窗口时,无论该页面是 Windows 上的窗口系统,还是 android 或者 ios 上的窗口系统,它们都需要处理各种事件,诸如有触发绘制页面的事件,有鼠标点击、拖拽、放大缩小的事件,有资源下载、文件读写的事件,等等。

消息队列解决了什么问题?

早期其实只有一个 UI 线程,js 的执行也是在 UI 线程中,那么,鼠标的各种事件,例如鼠标移动事件,每移动一像素就会触发一次事件,很显然,UI 线程是无法及时响应和处理这些事件的,所以消息队列的出现就是为了解决这个问题的。UI 线程会将这些待执行的事件添加到消息队列中,然后 UI 线程会不断循环地从消息队列中取出事件、执行事件。直到队列为空,当前 UI 线程也会被挂起。

通用 UI 线程架构

UI 线程每次从消息队列中取出事件,执行事件的过程称为一个任务。


通用的 UI 线程的结构,有消息队列,通过鼠标、键盘、触控板等产生的消息都会被添加进消息队列,主线程会循环地从消息队列中取出消息并执行。

异步回调函数的调用时机

异步回调有两种不同的类型,其典型代表是 setTimeout 和 XMLHttpRequest。

setTimeout

例子:比如在页面主线程中正在执行 A 任务,在执行 A 任务的过程中调用 setTimeout(foo, 3000)

function foo() 
    alert("Hello");

setTimeout(foo, 3000)

示意图:

在执行 setTimeout 函数的过程中,宿主就会将 foo 函数封装成一个事件,并添加到消息队列中,然后 setTimeout 函数执行结束。主线程会不间断地从消息队列中取出新的任务,执行新的任务,等到时机合适,便取出 setTimeout 设置的 foo 函数的回调的任务,然后就可以直接执行 foo 函数的调用。

定时器有单独的队列,每次执行新的宏任务时,主线程会先在这两个队列中查找即将要执行的事件,然后执行。

xmlHttpRequest

XMLHttpRequest 是用来下载网络资源的,而下载任务会消耗比较久,如果在 UI 线程上执行,会阻塞 UI 线程,拖慢 UI 界面的交互和绘制的效果。所以当主线程从消息队列中取出来了这类下载任务之后,会将其分配给网络线程。

示意图:

XMLHttpRequest 所触发的回调流程:

  1. UI 线程会从消息队列中取出一个任务,并分析该任务。
  2. 分析过程中发现该任务是一个下载请求,那么主线程就会将该任务交给网络线程去执行。
  3. 网络线程接到请求之后,便会和服务器端建立连接,并发出下载请求;
  4. 网络线程不断地收到服务器端传过来的数据;
  5. 网络线程每次接收到数据时,都会将设置的回调函数和返回的数据信息,如大小、返回了多少字节、返回的数据在内存中存放的位置等信息封装成一个新的事件,并将该事件放到消息队列中 ;
  6. UI 线程继续循环地读取消息队列中的事件,如果是下载状态的事件,那么 UI 线程会执行回调函数,程序员便可以在回调函数内部编写更新下载进度的状态的代码;
  7. 直到最后接收到下载结束事件,UI 线程会显示该页面下载完成。

网络线程在执行下载的过程中,会将一些中间信息和回调函数封装成新的消息,并将其添加进消息队列中,然后主线程从消息队列中取出回调事件,并执行回调函数。

以上是关于图解 Google V8 # 17:消息队列:V8是怎么实现回调函数的?的主要内容,如果未能解决你的问题,请参考以下文章

图解 Google V8 # 09:运行时环境:运行JavaScript代码的基石

图解Google V8,搞懂 JavaScript 执行逻辑

图解 Google V8 # 01:V8 是如何执行一段 JavaScript 代码的?

图解 Google V8 # 06:原型链:V8是如何实现对象继承的?

图解 Google V8 # 08:类型转换:V8是怎么实现 1 + “2” 的?

图解 Google V8 # 16:V8是怎么通过内联缓存来提升函数执行效率的?