# Event loop

也叫事件循环,在了解事件循环之前,要清楚一个问题,为什么需要异步?

举个例子,通常 javascript 代码是按顺序执行的。

console.log('1');
console.log('2');
console.log('3');
// 1 2 3

上面这三行代码的执行循序永远不会变。

现在我有一个网络请求,需要从服务器获取一个数据。

console.log('1');

// 向 http://example.com 网址发送了一个网络请求
axios('http://example.com')
.then( res =>{
    // res 是服务器返回的内容
    console.log(res);
})

console.log('3');

如果没有事件循环,分析一下上面的代码的执行结果:

首先执行第一行,打印 1,再执行第二个函数(axios),并等待结果,服务器给我们返回结果以后,打印返回的结果 res,最后打印 3(浏览器不是这样执行的)。

如果服务器访问速度很慢,执行到第二行,浏览器会处于一直等待状态,这时候用户点击一个网页上的点击按钮,这个操作等到服务器返回数据之后执行。

这种用户体验是很差,所以就有了 javascript 的事件循环机制。

事件循环有三部分组成:

  • 调用栈(Call Stack)
  • 消息队列(Message Queue)
  • 微任务队列(Microtack Queue)

普通函数执行时先放入调用栈中按顺序执行并立即释放。

# 1. 调用栈

也叫 Call Stack。普通函数执行时先放入调用栈中按顺序执行并立即释放。

function foo1(){
    console.log("22")
}
function foo() {
    console.log("11");
    foo1()
    console.log("33")
}
foo()

建议先自己做一下,再看答案。

这个应该不难理解,就是按顺序执行,这里也没有是么异步。

// 打印顺序是:11 22 33

# 2. 消息队列

也叫 Message Queue,这些事件放入消息队列 setTimeout,setInteval,xhr等,执行时放入消息队列中,执行完调用栈中的任务后执行消息队列中的函数。

再看下面的例子:

console.log("00")

setTimeout(() => {
    console.log("11")
}, 0)

console.log("22")

建议先自己做一下,再看答案。

消息队列

分析一下,首先遇到普通函数立即执行,所以打印 “00”。

遇到 setTimeout 放入消息队列中,往下执行。

打印 “22”,清空调用栈

最后运行消息队列中的任务,打印“11”。 微任务队列

打印顺序是:00 22 11

我们在做一道题:

console.log("00")

function foo() {
    console.log("11")
    setTimeout(() => {
        console.log("22")
    }, 0)
    console.log("33")
}

foo()

console.log("44")

建议先自己做一下,再看答案。

打印顺序是:00 11 33 44 22

# 3. 微任务队列

微任务队列也叫 Microtack Queue。微任务 promiseasync/await等属于微任务。

不了解 promise,先看一下这篇

在 JavaScript 执行过程中,如果遇到微任务先放入到微任务队列中,如果遇到宏任务放到消息队列中,调用栈清空后立执行微任务队中的内容,最后执行消息队列中的内容。

循序

结合这张图,看下面这个例子。

console.log("00")

setTimeout(() => {
    console.log(11)
}, 0)

var p = new Promise(resolve => {
    console.log(33)
    resolve(44)
})

p.then(res=>{
    console.log(res)
})

console.log(55)

建议先自己做一下,再看答案。

event loop

00
33
55
44
11

这就是 JavaScript 中的完整的一次事件循环,事件循环在面试中经常被问到,了解事件循环有利于写出更好的程序,这些例题来自有些来自于作者在面试过程中遇到的题。

更新时间: 12/26/2021, 1:44:08 PM