众所周知,JS引擎是单线程的,但是为了实现异步处理,会有其他线程来处理异步的事件,本篇来详细介绍JS处理异步函数的过程。首先了解几个概念:

宿主环境:JS运行的环境称为宿主环境,需要清楚的是JS不仅可以在浏览器上运行,也可以在服务器上运行。

执行栈(call stack):一个数据结构,用于存放各种函数的执行环境,每一个函数执行之前,它的相关信息会加入到执行栈。函数调用之前,创建执行环境,然后加入到执行栈;函数调用之后,销毁执行环境。

JS引擎执行顺序:执行的是执行栈的最顶部

异步函数:某些函数不会立即执行,需要等到某个时机到达后才会执行,这样的函数称之为异步函数,例如事件处理函数。异步函数的执行时机受宿主环境控制。

浏览器宿主环境的5个线程

  • JS引擎:负责执行执行栈最顶部代码
  • GUI线程:负责渲染页面
  • 事件监听线程:负责监听各种事件
  • 计时线程:负责计时
  • 网络线程:负责网络通信

接下来解释一下JS处理异步函数的过程:当浏览器的5个线程上发生了某个事件,如果该线程发现,这个事件有处理程序,它会将该处理程序加入一个叫做事件队列的内存,当JS引擎发现,执行栈中已经没有了任何内容后,会将事件队列中的第一个函数加入到执行栈中执行。JS引擎对事件的取出执行方式,以及与宿主环境的配合,称之为事件循环

事件队列在不同的宿主环境中有所差异,大部分宿主环境会将事件队列进行细分,在浏览器中,事件队列分为两种:

  • 宏任务(队列):macroTask,包括计时器结束的回调,事件回调,http回调等绝大部分异步函数
  • 微任务(队列):microTask,包括MutationObserver,Promise产生的回调

【注】当执行栈清空时,JS引擎首先会将微任务中的所有任务依次执行结束,如果没有微任务,则执行宏任务