前言

  • js引擎不是独立运行的,它运行在宿主环境中,这个环境可以是浏览器、可以是服务器,或者其他硬件设施。所以在浏览器的帮助下,js作为一种单线程语言,可以实现异步操作。
  • 浏览器内核是多线程的,几个常驻的线程:渲染引擎线程、js引擎线程、定时触发器线程、事件触发线程、异步http请求线程。

并发模型

MDN-并发模型

clipboard.png

  • 左边的栈存储的是同步任务。右边的堆用来存储声明的变量、对象。下面的队列就是任务队列,一旦某个异步任务有了响应就会被推入队列中。每个异步任务都和一个回调函数相关联。
  • 一个js程序的单线程用来执行栈中的同步任务,当所有同步任务执行完毕后,栈被清空,然后读取任务队列中的一个待处理任务,并把相关回调函数压入栈中,单线程开始执行新的同步任务,执行完毕。
  • 单线程从任务队列中读取任务是不断循环的,每次栈被清空后,都会在任务队列中读取新的任务,如果没有新的任务,就会等待,直到有新的任务,这就叫任务循环或者事件循环

事件循环(Event Loop)

事件循环的大致流程如下:

  1. 主线程执行所有同步任务,形成一个执行栈(并发模型中的stack)。
  2. 主线程执行同步任务的同时,子线程执行异步任务,并将相应的结果(事件)放入任务队列。
  3. 一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,把任务队列中事件相应的回调函数压入栈内开始执行。
  4. 执行回调后,栈空,继续重复第三步,形成一个循环。

Macrotask(task)

(macro)task(又称之为宏任务),可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)。
  • 浏览器为了能够使得JS内部(macro)task与DOM任务能够有序的执行,会在一个(macro)task执行结束后,在下一个(macro)task 执行开始前,对页面进行重新渲染。
  • (macro)task主要包含:script(整体代码)、setTimeout、setInterval、I/O、UI交互事件、postMessage、MessageChannel、setImmediate(Node.js 环境)

microtask

microtask(又称为微任务),可以理解是在当前 task 执行结束后立即执行的任务。也就是说,在当前task任务后,下一个task之前,在渲染之前。
  • 所以它的响应速度相比setTimeout(setTimeout是task)会更快,因为无需等渲染。也就是说,在某一个macrotask执行完后,就会将在它执行期间产生的所有microtask都执行完毕(在渲染前)。
  • microtask主要包含:Promise.then、MutaionObserver、process.nextTick(Node.js 环境)

qikke
10 声望3 粉丝