事件循环机制 Event Loop

65 阅读3分钟

一、了解事件循环

事件循环用于协调异步任务的执行顺序、传递消息以及处理用户交互等事件。由以下四个组成部分组成。

  • 调用栈(Call Stack)

    调用栈是 JavaScript 的一种执行机制,用于控制函数的执行顺序。当函数被调用时,将函数压入调用栈中,并开始执行函数,函数执行完成后从调用栈中弹出并返回结果。如果调用栈为空,则脚本停止执行。

  • 任务队列(Task Queue)

    任务队列是 JavaScript 的一种执行机制,用于存储异步任务的回调函数。当异步任务完成时,将回调函数添加到任务队列中等待被执行。

  • 事件循环(Event Loop)

    事件循环是 JavaScript 的一种执行机制,用于协调调用栈和任务队列的执行顺序。事件循环不断地从任务队列中取出第一个回调函数,并将其压入调用栈中执行,直到任务队列为空为止。

  • 微任务队列(Microtask Queue)

    微任务队列是在事件循环的回调函数执行过程中产生的,用于存储微任务的回调函数。当微任务产生后,会被添加到微任务队列中,等待事件循环调用栈为空时执行。微任务包括 Promise、MutationObserver 等。

二、宏任务和微任务

宏任务和微任务都属于异步任务的一种,它们的区别在于任务队列不同,执行的时机也不同。

宏任务(Macro Task)

宏任务指的是那些需要长时间运行的任务,每个宏任务执行完成后,都会清空微任务,然后从宏任务队列中取出下一个任务执行。

  • setInterval() / setTimeout()
  • setImmediate()
  • ajax
  • 事件绑定
  • UI rendering/UI事件
  • postMessage、MessageChannel
  • setImmediate、I/O(Node.js)
  • script (可以理解为外层同步代码)

微任务(Micro Task)

微任务指的是那些需要尽可能快速执行的任务,当每个宏任务执行完毕并清空微任务队列,会接着执行当前微任务队列中的任务,直到执行完毕为止。

  • new Promise()后的then()与catch()函数
  • new MutaionObserver()
  • process.nextTick(Nodejs)
  • Object.observe(已废弃;Proxy 对象替代)

三、JavaScript 中任务的执行顺序如下:

  1. 执行同步任务,即按照顺序执行的任务。
  2. 执行当前宏任务队列中的第一个任务,例如 setTimeoutsetIntervalsetImmediate 等。
  3. 执行所有可立即执行的微任务队列中的任务,例如 Promisethen()catch()process.nextTick 等。
  4. 回到宏任务队列中,执行下一个宏任务,重复步骤 2 和 3。

四、巩固练习

示例一:async await 相关

不管await后面跟着的是什么,await都会阻塞后面的代码 (即加入微任务队列),先执行 async外面的同步代码,同步代码执行完,再回到 async 函数中,再执行之前阻塞的代码

 async function fn1 (){
     console.log(1)
     await fn2()
     console.log(2) // 阻塞
 }
 ​
 async function fn2 (){
     console.log('fn2')
 }
 ​
 fn1()
 console.log(3)

上述输出结果为:1fn232

示例二:同步、宏任务、微任务输入顺序

 async function async1() {
     console.log('async1 start')
     await async2()
     console.log('async1 end')
 }
 ​
 async function async2() {
     console.log('async2')
 }
 ​
 console.log('script start')
 setTimeout(function () {
     console.log('settimeout')
 })
 ​
 async1()
 ​
 new Promise(function (resolve) {
     console.log('promise1')
     resolve()
 }).then(function () {
     console.log('promise2')
 })
 ​
 console.log('script end')

上述输出结果为:script startasync1 startasync2promise1script endasync1 endpromise2settimeout