【js】EventLoop事件循环

200 阅读2分钟

js中的任务

js是单线程异步编程:借用浏览器的多线程机制,再基于EventLoop事件循环机制,实现单线程异步效果。

异步微任务异步宏任务
requestAnimationFramesetTimeout/setInterval
promise.then/finally/catch事件绑定
async/awaitXMLHttpRequest/Fetch请求
MutationObserverIO操作
process.nextTickscript(整体代码)

EventLoop事件循环过程

浏览器加载页面,除了开辟堆栈内存,还开辟了两个队列

  • WebApi:任务监听队列:监听异步的任务是否可以执行。
  • EventQueue:所有可执行的异步任务:需要在这里排队等待执行。(异步微任务/异步宏任务)

流程:

  • js主线程自上而下执行代码中,遇到异步任务会把异步任务放到WebApi中进行监听。(开辟新的线程去监听,不会阻碍主线程代码的执行。)
  • 当WebAp中监听任务(setTimeout)可以执行了,也不会立即执行,而是放到EventQueue任务队列中进行排队。(根据微任务/宏任务分类放在不同的任务队列中)
    • 例如:定时器,设定一个等待时间,时间到了也不会立即执行。。。。
  • 当同步代码(同步宏任务)执行完毕,主线程空闲下来之后,此时去循环执行EventQueue队列中的任务。
    • 按照异步微任务->异步宏任务执行(每次执行一个宏任务之前都要把微任务队列执行完)
    • 同样级别按谁先放入谁先执行

事件循环示例

  • 宏任务执行流程 image.png
  • Promise微任务执行流程

promise.then(onfullfilled,onrejected),回调函数先进入WebApi中,监听promise的状态是成功后,把onfullfilled放入EventQueue的微任务队列中等待执行。

console.log(1)
new Promise((resolve, reject) => {
    console.log(2) // 执行器函数是同步代码
    resolve("ok")
}).then(res => {
    // .then是异步微任务
    console.log(3)
})
console.log(4)
/* 输出
* 1 -> 2 -> 4 ->3 
*/
  • 遇到await
  1. 会立即执行await后面的代码,看返回的promise实例(不是promise实例也会变成promise实例)是否成功。
  2. 会把当前执行上下文中,await下面的代码当作异步微任务。放到EventQueue任务微队列中,当同步代码执行完再循环执行。

image.png

async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
// 语法糖:上面的代码等价于 ==>
async function async1() {
    console.log('async1 start');
    Promise.resolve(async2()).then(() => {
        console.log('async1 end')
    })
}
  • 在执行宏任务中如果产生了微任务,需要先执行完微任务队列中的所有微任务,才执行下一个宏任务。

image.png *如有不对的地方,欢迎交流。