JavaScript 的线程
作为浏览器语言,JavaScript 会对 DOM 进行操作,如果存在多个线程同时操作 DOM,一个线程创建了一个节点,另外一个线程去删除这个节点,浏览器渲染就会乱套。所以,JavaScript 被设计成单线程。
单线程指的是 JavaScript 引擎解析和执行代码的线程只有一个,这个线程为主线程,所有的同步任务都在主线程上执行。
同步和异步
单线程就意味着所有的任务都是同步的,下一个任务必须在上一个任务完成后再执行。假如有一个同步的网络请求,我们等待的时间是不确定的,在请求响应前,浏览器就会处于阻塞状态。
所以 JavaScript 提供了异步任务,我们不需要关心异步任务何时响应,这样就可以保证主线程的任务畅通无阻的执行下去。
任务队列
异步任务不进入主线程,而是进入任务队列,当异步任务完成,任务队列通知主线程,该任务才会进入主线程。队列的特点是先进先出,任务队列中的事件会之按照放入的时间先后顺序执行。
Event Loop
由于主线程读取任务队列的的过程是循环不断的,所以这种机制被称为 Event Loop(事件循环)。
- 所有同步任务都在主线程(stack)上执行,形成一个执行栈。
- 主线程之外,还存在一个任务队列(callback queue),只要异步任务有了运行结果,就在任务队列之中放置一个事件。
- 一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,将队列中的事件放到执行栈中依次执行。
- 主线程从任务队列中读取事件,这个过程是循环不断的。
宏任务和微任务
任务队列可分为宏任务队列和微任务队列
- 宏任务:setTimeout、setInterval、setImmediate、I/O 等。
- 微任务:then、process.nextTick、Object.observer 等。
在浏览器环境中,当执行栈的任务执行完毕,先去读取微任务队列,微任务执行完毕,再从宏任务队列读取一个事件放到执行栈去执行,执行完毕后再去取微任务队列,如此重复。
在 Node 环境中,Event Loop 分为 6 个阶段,每一个阶段对应着一个宏任务队列。
与浏览器环境不同,Node 环境下,当执行栈的任务执行完毕后,先清空微任务队列。再取到宏任务队列所有事件去执行,执行完毕转到下一个阶段,此时再去清空微任务队列。