1. 几个重要概念

1.1 单线程

JavaScript是单线程语言,因为JavaScript设计的初衷就是处理和用户交互的操作,如dom操作、ajax请求;如果设计为多线程会因为共享浏览器的资源造成死锁。当然,单线程的计算能力有限,js采用任务队列+事件轮询方案1来解决多任务非阻塞执行的问题。

1.2 执行栈

同步函数执行时会创建一个帧,这个帧包括了形参、局部变量(预编译过程),然后帧会被压入执行栈,形成栈帧。当函数执行完时,帧被弹出。线程执行时在内存结构上形成了栈(stack),进程则形成堆(heap)。

1.3 event loop线程

js虽然是单线程语言,但js引擎是多线程的。其中event loop线程负责主线程和其他线程的通信,实现异步函数的非阻塞执行。对于单线程的js,如果通过启用多个进程或者在一个进程去开启多个进程来执行多任务,不仅会占用资源也会闲置资源造成浪费。所以设计者另开一个event loop线程,通过事件轮询机制检测消息队列是否存在消息从而执行消息对应的回调函数,实现异步函数的执行。

1.4 宏任务和微任务

js事件分为下面两种:

  • 宏任务macro-task:整体代码script,setTimeout,setInterval
  • 微任务micro-task:Promise.then(非new Promise),process.nextTick(node环境)

不同的任务有不同的任务队列。事件轮询的顺序是先检测宏任务队列,再到微任务队列。

2. 将几个重要概念联系起来

event loop是JavaScript的运行机制。

JavaScript进程与线程 javascript线程机制_js进阶


如上面的流程图,一份js代码的执行包括:

第一轮事件循环:整体script作为第一个宏任务进入主线程,遇到同步函数分布到主线程的执行栈去执行,遇到异步函数分布到event table,注册异步任务的回调函数到宏任务队列或微任务队列。第一轮事件循环宏任务(整体script)结束。然后轮询微任务队列,如果存在微任务即执行所有的微任务,第一轮事件循环正式结束;否则直接进行第二轮事件循环。

第二轮事件循环:取第一轮所得的宏任务队列的一个任务(忽略js环境一般是队首)到主线程执行,第二轮事件循环宏任务结束。然后轮询微任务队列并执行,第二轮事件循环正式结束。
。。。。。。

周而复始的进入、分布、执行;进入、分布、执行,直到js代码执行完毕。

常驻线程中主线程和event loop线程是实现多任务非阻塞执行的主要线程。一个异步任务的执行通常需要多个线程共同完成,如ajax请求需要js主线程、http请求线程、事件触发线程和event loop线程共同完成的。

掌握event loop的机制,对编码、测试和debug能力的提升都大有裨益2


  1. 解决多任务执行的三种方案分别是:任务队列+事件轮询、启用多进程、一个进程包含多个线程;后两种会造成系统资源的大量浪费。 ↩︎
  2. 本文的内容参考了包括ssssyoki等优秀博主的博文不一一鸣谢。个人在图上做了自己理解的标记,有疑惑可以评论区多多交流哈 (๑•ㅂ•́)و✧ ↩︎