js 事件循环机制

351 阅读2分钟
  1. 执行栈和事件队列 当js调用一个函数的时候会产生执行环境,即执行上下文,被执行的方法被推到一个执行栈中依次执行。 当碰到异步任务的时候,会将这些任务放到event quene中,即事件队列中。

异步任务有分为宏任务和微任务,会被分别放在各自对应的event queue。

执行完同步任务后,会找到微任务队列,如果队列中存在对应事件,则先执行微任务,当微任务列表清空后会执行下一个宏任务。

常见的微任务有: promise、MutaionObserver等 常见的宏任务有: setInterval、setTimeout等

  1. 举例
console.log(1)
setTimeout(() => {
    console.log(2)
}, 0)

new Promise((resolve, reject) => {
    console.log(3)
    resolve(3)
}).then(res => {
    console.log(4)
})

上述代码会优先执行调用栈中的同步任务,即1, 3

promise中函数会立即执行,也属于同步任务,所以会先输出3。

之后查找微任务队列里的任务,清空微任务列表,之后会输出4, 最后输出宏任务的3。所以最后结果是1342。

在看一个复杂的例子

console.log(1)

let timer1 = setTimeout(() => {
    console.log(2)
    let promise3 = new Promise((resolve, reject) => {
        console.log(3)
        resolve(3)
    }).then(res => {
        console.log(4)
        let promise4 = new Promise((resolve, reject) => {
            console.log(11)
            resolve(11)
        }).then(() => {
            console.log(12)
        })
    })
}, 0)

let promise1 = new Promise((resolve, reject) => {
    console.log(5)
    resolve()
}).then(res => {
    console.log(6)
    let timer2 = setTimeout(() => {
        console.log(7)
        let promise2 = new Promise((resolve, reject) => {
            console.log(8)
            resolve()
        }).then(res => {
            console.log(9)
        })
    }, 0)
})

console.log(10)
  1. 首先执行执行栈中的任务,所以会先输出 1 5 10;同时现在任务队列里被同时放进一个宏任务,一个微任务。
宏任务队列微任务队列
timer1promise1
  1. 之后会执行微任务队列里面的任务,所以先输出6,然后又遇到一个宏任务timer2,放到宏任务队列,此时微任务队列里任务清空。
宏任务队列微任务队列
timer1
timer2
  1. 此时将宏任务队列里的timer1取出来,放到执行栈中执行,会输出2 3,然后又遇到一个微任务promise3, 将promise3放入微任务队列。
宏任务队列微任务队列
timer2promise3
  1. 执行完一个宏任务,下次事件循环该清空微任务队列里,此时微任务队列里有一个promise3,执行promise3输出4, 同时又遇到一个微任务放到微任务队列中,此时微任务队列未清空,所以继续执行微任务队列里的任务,直至微任务队列清空。所以会输出11 12
宏任务队列微任务队列
timer2
  1. 微任务队列清空,下面会执行宏任务timer2,其中timer2中又有一个微任务所以会输出7 8 9

所以这个列子最后输出的结果是 1、5、10、6、2、3、4、11、12、7、8、9