Event Loop解读(待完善)

本文最后更新于:9 个月前

浏览器Event Loop

定义

To coordinate events, user interaction, scripts, rendering, networking, and so forth, user agents must use event loops as described in this section. Each agent has an associated event loop, which is unique to that agent.

要想了解浏览器的事件机制,我们需要知道,浏览器的线程基本概念

浏览器进程线程

浏览器中是多线程多进程的,我们每打开一个新的页面就会打开一个新的进程,每个进程中都会有多个线程在运作,其中常见的线程有以下几种:

  1. GUI渲染线程
  • 负责页面渲染,处理HTML,CSS解析,DOM树的构建、布局和绘制;
  • 当页面发生重绘重排时会重新调用此线程;
  • 该线程和JS线程互斥,所以当GUI执行时,JS会被挂起,JS线程执行时,GUI会被挂起,直到任务队列空了,才会去执行GUI
  1. JS线程
  • 负责js代码的执行;
  • 执行准备好的事件;
  1. 定时器线程
  • 负责执行定时器任务例如:setTimeout,setInterval;
  • 主线程执行遇到定时器任务时,将任务加入定时器线程,在计时完毕后,任务会加入任务队列,等待JS线程的执行
  1. 事件触发线程

负责将准备好的事件添加到任务队列,等待JS线程执行

  1. 异步请求线程

负责执行异步请求一类的操作,如Promis,ajax,axios
主线程执行到了异步任务后,将任务讲给异步请求线程执行,执行完后,将回调函数添加到微任务队列,等待JS线程执行

事件队列

浏览器中的Event-loop
浏览器中的事件循环机制中有两中队列,一个是宏任务一个是微任务,当主线程遇到同步操作时加入宏任务,遇到异步操作时加入微任务。常见的宏任务:setTimeout,setInterval,script(整段代码),I/O操作,UI渲染常见的微任务:Promise.then,MutationObserver
  其中的执行机制是当某个宏任务执行完后,会查看是否有微任务队列。如果有,先执行微任务队列中的所有任务,如果没有,会读取宏任务队列中排在最前的任务,执行宏任务的过程中,遇到微任务,依次加入微任务队列。栈空后,再次读取微任务队列里的任务,依次类推。其中微任务是不打断的直接执行完一整个队列的任务。

宏任务

微任务

一些题目解读

可以通过一个例子来进行分析:

  1. console.log('script start');
        async function async1(){
            await  async2().then(_=>console.log(_));
            console.log('async1 end')
            return 'async1 return';
        }
        async function async2(){
            Promise.resolve(console.log('async2 promise')).then(_=>console.log('async2 then'));
            await console.log('async2 await');
            console.log('async2 end');
            return 'async2 return';
        }
        async1().then(_=>console.log(_,'async1 then'));
    
        setTimeout(function(){
            console.log('settimeout');
        },0);
    
        new Promise(resolve => {
            console.log('promise');
            resolve();
        }).then(function () {
            console.log('function1');
        }).then(function(){
            console.log('function2');
        });
        console.log('script end')
    在这个例子里:
  • 首先输出script start
  • 然后是async1的内容,此刻await执行async2输出async2 promise,然后then进入微任务
  • 然后下面的await输出async2 await,然后后面的阻塞,进入微任务
  • 然后继续执行,输出promisethen进入微任务
  • 然后输出script end
  • 此时宏任务执行完一轮
  • 微任务开始执行,先输出async2 thenasync2 end,然后return值进入微任务,同一批次的微任务中继续执行输出function1,然后2then又进入微任务
  • 继续执行微任务,此时先输出async2 return,然后async1await后的进入微任务
  • 输出function2
  • 在执行微任务输出async1 end,然后1return进入async1().then(_=>console.log(_,'async1 then'));的微任务
  • 输出async1 return,async1 then,微任务为空,执行宏任务
  • 输出settimeout

最终顺序为

script start
async2 promise
async2 await
promise
script end
async2 then
async2 end
function1
async2 return
function2
async1 end
async1 return
async1 then
settimeout
  1. console.log('e1')
    new Promise((r,j)=>{r()}).then(()=>{
        console.log('promise3');
        setTimeout(()=>{
            console.log('timeout3')
        },0)
    })
    setTimeout(()=>{
        console.log('timeout1')
        new Promise((r,j)=>{r()}).then(()=>{
            console.log('promise1')
        })
    },5000)
    console.log('e2')
    new Promise((r,j)=>{r(console.log('1'))}).then(()=>{
        console.log('promise2');
        setTimeout(()=>{
            console.log('timeout2')
        },0)
    })
    console.log('e3')
    在这个例子里:
  • 主线程main进入栈执行操作,此时执行script代码,会把输出e1
  • 之后遇到Promise.then,会将其加入微任务队列
  • 再往下执行,遇到setTimeout,将其加入定时器线程,等待计数完成后加入宏任务队列
  • 再往下执行输出e2
  • 往下执行遇到Promise,先将resolve输出1,然后将then加入微任务队列
  • 再往下执行输出e3,此时script代码执行完毕,立刻搜索微任务是否为空,不为空就立刻执行此时微任务的队列
  • 此时微任务队列先进先出输出promise3,然后遇到setTimeout,将其加入定时器线程,等待计数完成后加入宏任务队列
  • 再往下执行第二个微任务,输出promise2,然后遇到setTimeout,将其加入定时器线程,等待计数完成后加入宏任务队列
  • 再往下执行发现微任务队列为空,去宏任务队列去寻找是否有任务
  • 在宏任务发现此时先执行完的setTimeout3,输出timeout3,将then加入微任务队列
  • 执行完该次宏任务后立刻寻找微任务队列,为空,去寻找宏任务队列
  • 在宏任务发现先执行完的setTimeout2任务,执行输出timeout2
  • 此时任务队列为空,等待新任务加入
  • 此时setTimeout1计数完毕,加入宏任务队列,并执行,输出timeout1,将then加入微任务队列
  • 执行完该次宏任务后立刻寻找微任务队列,并执行,输出promise1
    最终结果为
    e1 
    e2 
    1 
    e3 
    promise3 
    promise2 
    timeout3 
    timeout2 
    timeout1 
    promise

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!