chenyinkai / blog

学习笔记,技术心得,疑难困惑,生活总结。喜欢的请star。。
42 stars 1 forks source link

理解js中的事件循环 #43

Open chenyinkai opened 6 years ago

chenyinkai commented 6 years ago

javascript 事件循环

可以先来看这样的一段代码, 可以思考下会输出什么结果:

console.log('event start')
setTimeout(function () {
    console.log('setTimeout');
});

new Promise(function(resolve,reject){
    console.log('promise start')
    resolve()
}).then(function(){
    console.log('promise end')
})
console.log('event end')

正确的输出结果如下:

// event start
// promise start
// event end
// promise end
// setTimeout

那么, 为什么会得到这样的结果呢? 这就和 javascript 的执行机制密切相关了.

事件队列和事件循环

javascript 是一门单线程的语言, 这就意味着在执行代码的时候, 都只有一个主线程来处理所有的任务.

我们都知道 javascript 包括同步代码和异步代码, 那么 javascript 是怎么处理这两种情况的呢?

可以通过下图简单说明:

event loop

这里我们引进了 Event Queue 事件队列这一概念. 所有异步操作的回调都会进入到这里. 然后等到主线程空闲, 就会从这里调取回调执行.

event loop

可以通过上图中了解 event loop, 事件队列以及主线程之间的关系.

setTimeout

setTimeout 相信大家都有使用过, 可以延时执行并且是异步执行的.

但是有时候我们得到的结果往往是代码实际执行的时间比我们想要延时执行的时间要久。这又是为什么呢?

这就和我们之前所说的 Event Loop 有关了, 我们可以来具体看下 setTimeout 的执行步骤:

setTimeout(function () {
    asyncFn()
}, 1000);

syncFn()

所以有时候会出现代码实际执行时间比延时时间长的情况.

macro-task 和 micro-task

以上我们只是简单的分为了同步任务和异步任务. 其实我们还有更加具体的定义:

之前我们说过异步任务会进入到事件队列中, 不同类型的任务会进入到不同的队列中, 比如宏任务会进入到宏任务队列中, 微任务会进入到微任务队列中.

我们可以参考下图来理解事件循环, 宏任务, 微任务的关系:

event loop

我们只要记住 当当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件,然后再去宏任务队列中取出一个事件。同一次事件循环中,微任务永远在宏任务之前执行

这时候我们就可以解释一开始的代码执行结果了:

console.log('event start')
setTimeout(function () {
    console.log('setTimeout');
});

new Promise(function(resolve,reject){
    console.log('promise start')
    resolve()
}).then(function(){
    console.log('promise end')
})
console.log('event end')

参考文献