浏览器 :何为事件循环?它能做什么?

233 阅读3分钟

为什么需要事件循环

因为js是单线程 非阻塞的。

单线程:意味着,js代码执行的时候,都只有一个主线程来处理所有的任务,保证了程序执行的一致性(不会出现两个线程同时对同一个dom操作)。

非阻塞:再代码需要进行异步任务(无法立刻获取返回结果)的时候,需要挂起,在主线程空闲时根据一定的规则去指向相应的回调。js 正是通过event loop(事件循环)来实现非阻塞。

什么是事件循环

引用MDN上的说法:

事件循环用来收集事件(用户事件/其他非用户事件等)、对任务进行排队在合适的时候进行回调。 然后它执行所有处于等待中的JavaScript任务(宏任务),然后是微任务,然后开始下一次循环之前执行的一些必要渲染和绘制操作。

事件循环用来收集事件、对收集到的任务进行排队分两个队伍(宏任务、微任务),再主线程执行栈空的时候从微任务队列中取出最前面的一个事件,
在当前执行栈为空的时候,继续去微任务队列取事件直到执行空了,然后去宏任务队列中取出最前面的一个事件,把对应的回调加入当前执行栈,
在当前执行栈为空的时候,继续去微任务队列取事件直到执行空了,然后去宏任务队列中取出最前面的一个事件...如此反复,进入循环。

执行一个宏任务(先执行同步代码)-->执行所有微任务-->UI render-->执行下一个宏任务-->执行所有微任务-->UI render-->....

微任务有哪些?

promise().then(回调); async/await; MutationObserver (html5 新特性); process.nextTick;

宏任务有哪些?

js整体代码; setTimeOut, setInterval; postMessage; I/O操作; UI交互事件;

宏任务和微任务需要注意知识点

1. 宏任务、微任务的存储方式是队列(先进先出)。
2. js整体代码是宏任务,即第一次主线程执行栈空的时候 直接去执行微任务队列
3. 执行微任务队列时遇见新的微任务会放在微任务队尾本次会执行到。
4. promise().then 里面的才是微任务
    new Promise(resolve => {
      console.log('Promise') // 立即执行
      resolve()
    }).then(function() {
      // 这里面的才是微任务
      console.log('promise1')
    })
5. await 注意:
  分两种情况:
      1.如果 await 后面直接跟的为一个变量,比如:await 1;这种情况的话相当于直接把await后面的代码注释成微任务如下
        async function async1() {
          await async2()  // 立即执行
          console.log('async1 end') // 直接注册成微任务
        }
        async function async2() {
          console.log('async2 end')
          return 1
        }
      2. 如果 await 后面的是一个异步函数的调用
        
     

微任务优先级

宏任务优先级

为什么需要有微任务队列

为了插队,如果只有一个队列,当一个promise().then(promise().then(回调2))的时候,回调2 需要排到任务队尾,如果前面的任务多,执行情况得不到保证,在宏观上看没有做到一个立即接一个地执行,而有了微任务来插队在执行上看起来像是同步。

小结练习

console.log('script start')

async function async1() {
    await async2()
    console.log('async1 end')
}
async function async2() {
    console.log('async2 end')
    return Promise.resolve().then(()=>{
        console.log('async2 end1')
    })
}
async1()

setTimeout(function() {
    console.log('setTimeout')
}, 0)

new Promise(resolve => {
    console.log('Promise')
    resolve()
})
.then(function() {
    console.log('promise1')
})
.then(function() {
    console.log('promise2')
})

console.log('script end')

script start => async2 end => Promise => script end => async2 end1 => promise1 => promise2 => async1 end => setTimeout

node事件循环机制

参考文献

有如下三种事件循环:

  • Window 事件循环
  • Worker 事件循环
  • Worklet 事件循环

理论部分参考:

developer.mozilla.org/en-US/docs/…

developer.mozilla.org/zh-CN/docs/…

实践部分参考:

juejin.cn/post/684490…

nodejs.org/zh-cn/docs/…