浏览器中的事件循环(Event Loop)

EventLoop 解决了什么问题(背景)?

JavaScript单线程是一门单线程的语言,同一个时间只能做一件事情,如果当一个语句需要执行很长时间的话(比如:请求、定时器、读取文件等等),后面的语句就得一直等着前面的语句执行结束后才会开始执行。因此,JavaScript将所有执行任务分为了同步任务异步任务。而Event Loop就是浏览器或Node解决JS单线程运行时不会阻塞的一种机制

什么是同步任务和异步任务

同步任务:又叫做非耗时任务,指的是在主线程上排队执行的那些任务,只有前一个任务执行完毕,才能执行后一个任务

异步任务:又叫做耗时任务,异步任务由JavaScript 委托给宿主环境进行执行,当异步任务执行完成后,会通知JavaScript 主线程执行异步任务的回调函数

什么是宏任务和微任务

JavaScript 把异步任务又做了进一步的划分,异步任务又分为两类:宏任务和微任务

宏任务:当前调用栈执行的任务

微任务:需要在此次宏任务执行结束后,下一次宏任务执行前,执行的任务

宏任务 微任务
定时器(setTimeout、setInterval) Promise(then、catch、finally)
setImmediate MutationObserver
requestAnimationFrame process.nextTick(Node)
script标签下整块代码

事件循环的进程模型

event-loop.png
  • 从宏任务队列中,按照入队顺序,找到第一个执行的宏任务,放入调用栈,开始执行
  • 执行遇到异步任务,如果是微任务,将放到微任务队列中,如果是宏任务,则放到宏任务队列中
  • 同步任务执行完成后(即调用栈清空后),该宏任务被推出宏任务队列,然后微任务队列开始按照入队顺序,依次执行其中的微任务,直至微任务队列清空为止
  • 当微任务队列清空后,一个事件循环结束
  • 接着从宏任务队列中,找到下一个执行的宏任务,开始第二个事件循环,直至宏任务队列清空为止

常见面试题

// 整体代码是一个宏任务,放入到宏任务队列中,进入调用栈执行
console.log(1);  
setTimeout(()=>{
    console.log(2);
    Promise.resolve().then(res => { console.log(3) });
    setTimeout(()=>{  
        console.log(4); 
        Promise.resolve().then(res => { console.log(5) });
    }, 0)
}, 30)

Promise.resolve().then(res=>{ console.log(6) }) 

const fn = async () => {
    console.log(7)  
    // 遇到了await 会等待promise完成,同时交出执行权
    await new Promise((resolve)=>{
        // new Promise 中的代码立即执行
        console.log(8) 
        setTimeout(()=>{
            resolve(); 
            console.log(9)
        }, 20)
    }).then(res => console.log(10));
    console.log(11)
}
// fn执行
fn();

setTimeout(()=>{
    console.log(12); 
    new Promise((resolve) => {
        console.log(13)
        resolve();
    }).then(res => { console.log(14) })
}, 0)
// 打印结果:1 7 8 6 12 13 14 9 10 11 2 3 4 5

你可能感兴趣的:(浏览器中的事件循环(Event Loop))