事件循环(Event Loop)

74 阅读2分钟

一、什么是Event Loop🤔

JavaScript是一门单线程语言,它一个时间只能做一件事;

在js代码运行的时候,会形成一个执行栈,每调用一个函数,就会把该函数的执行上下文放入执行栈;

另外还有任务队列存放待完成的任务,在js中,有同步任务异步任务,异步任务又分为宏任务微任务

因为script代码整体就是一个宏任务,因此事件循环先从一个宏任务开始,当执行完宏任务后,就从微任务队列拿微任务到执行栈中执行,当前微任务队列所有微任务执行完后,就执行宏任务队列的下一个宏任务;

然后重复这个操作,直到任务队列里的任务执行完,这个过程就叫事件循环。

二、宏任务和微任务📜

JavaScript 的事件分两种,宏任务(macro-task)和微任务(micro-task)。

1. 宏任务

  • script
  • setTimeout
  • setInterval
  • setImmediate

2. 微任务

  • promise.then() promise.catch() 的回调cb
  • async await
  • mutationObserver ==> 监听DOM的改变
  • process.nextTick() ==> Node

关系: 微任务是包含在宏任务里面的,一个宏任务中,可以有多个微任务。

3. 执行顺序

  • 首先script代码块可以看做第一个宏任务,开始第一个Tick事件循环
  • 会先执行script代码块中的同步代码
  • 如果遇到宏任务,就放到宏任务队列中等待执行, 如果遇到微任务,放到微任务队列中
  • 当主线程执行完同步代码的时候,首先去微任务队列中清空当前事件循环的所有微任务(本轮事件循环Tick结束)
  • 如果还有异步的宏任务,那么就会进行循环执行上述的操作

三、来点例子🌰

eg1:

const promise = new Promise((resolve, reject) => {
    console.log(1);
    resolve(5);
    console.log(2);
}).then(val => {
    console.log(val);
});

promise.then(() => {
    console.log(3);
});

console.log(4);

setTimeout(function() {
    console.log(6);
});

// 1  2  4  5  3  6

执行结果为: 1 2 4 5 3 6

event1.png

eg2:

async function async1() {
    console.log('async1 start')  
    await async2()      // await这一行,这个async2 同步执行的; await的下面,放到微任务队列
    console.log('async1 end') // 这一行,相当于放到.then()中  微1 
    console.log(666)
}
async function async2() {
    console.log('async2')
}

console.log('script start')  

setTimeout(function() {
    console.log('setTimeout') // 放到宏任务队列中 宏1 
}, 0)

async1();

new Promise(function(resolve) {
    console.log('promise1')
    resolve();
}).then(function() {
    console.log('promise2')  // 微任务  微2 
});
console.log('script end')

// script start 
// async1 start
// async2
// promise1
// script end
// async1 end
// 666
// promise2  (本轮tick结束)
// setTimeout

执行结果为:

event2.png

event3.png

✨✨✨✨✨✨✨✨✨✨

在本轮事件循环中的微任务比下一次的事件循环中的宏任务优先级高,也就是我们要将这轮的微任务都执行完毕,才能去执行下一个宏任务。