JavaScript的事件循环是什么?

111 阅读3分钟

翻译自:www.educative.io/answers/wha…

事件循环(EventLoop)是JavaScript异步编程背后的秘密。JS在一个线程上执行所有的操作,但是使用了一些聪明的数据结构,给了我们多线程的错觉。

调用栈(Call Stack)

调用堆栈负责跟踪要执行的所有操作。当一个函数完成时,它将从堆栈中弹出。

image.png

事件队列(Event Queue)

事件队列负责发送新函数到调用栈中处理。它遵循队列的数据结构特性(先进先出)来维护所有的操作按照正确的顺序执行。

image.png

当一个异步函数被调用时,他会被发送到对应的内置的浏览器API。这些API基于从调用栈收到的命令,启动各自的单线程操作。

比如:setTimeout。当堆栈中需要处理一个settimeout操作时,它会被派发到对应的API,并等到指定的时间将该操作送回来处理

API将操作派发到哪里了呢? ————事件队列。因此,在JS中我们有了一个执行异步操作的循环系统。JS本身是单线程的,但是浏览器API扮演了多线程的角色。

事件循环促进了这个过程;它不断地检查调用栈是否为空。如果为空,新函数将被推入事件队列;否则执行当前函数

image.png 深入理解js事件循环机制(浏览器篇)中有个动态图很形象:

一个 Event Loop 中,可以有一个或者多个任务队列(task queue),一个任务队列便是一系列有序任务(task)的集合;每个任务都有一个任务源(task source),源自同一个任务源的 task 必须放到同一个任务队列,从不同源来的则被添加到不同队列。  setTimeout/Promise 等API便是任务源,而进入任务队列的是他们指定的具体执行任务。(引用自:github.com/Advanced-Fr…)

一次事件循环过程:

  1. 执行一个宏任务(栈中没有就从事件队列中获取)
  2. 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中,微任务队列在下一个宏任务之前,遇到宏任务则放在队列尾部
  3. 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
  4. 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
  5. 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)

任务队列

通常情况下,任务队列中可能存在多个宏任务,但只有一个微任务队列。

宏任务(macro task / task)

通常指浏览器定义的行为 常见宏任务包括:

  • script(整块代码)
  • 计时器(setTimeoutsetInterval
  • I/O操作
  • DOM事件(UI交互事件)
  • setImmediate(node环境)
  • postMessage
  • MessageChannel

微任务(micro task / job)

当前宏任务执行结束后立即执行 包括:

  • Promise.then/catch/finally
  • process.nextTick(node中优先级比promise回调更高的微任务)

  • 宏任务
  • 微任务
  • 异步promise面试题

自用笔记,看客留情,仍有待拓展