javascript事件循环

为什么同步,为什么有异步

JavaScript是单线程的,这是源于基于用户行为决定的,以及DOM的操作,这决定了js的单线程的机制,如果js中有多个线程,此时a线程在执行dom节点的添加操作,b线程执行dom的删除操作,浏览器该如何执行操作?

拿坐电梯来说,从1楼到18楼,只有一部电梯,人很多很多,第一班电梯上不了,只能等第二班,也就是别人上去了之后,电梯空了,她们才能上去,其他的任务才会继续进入”电梯“直到到达18楼任务完成,后面的任务才会开始执行

这会导致如果前面代码出现执行缓慢,后面代码被阻塞,一直等待无法执行
这些代码都是同步代码,异步代码在js中是如何执行的,js的另一个特点是非阻塞,实现的关键点在于事件队列(Task Queue)

事件队列 TO 事件循环

js在执行异步代码的时候,不会一直等待异步代码的结果,而是放在一边(事件挂起),继续走自己主流程(栈中的其他任务),当异步的结果返回之后,js会将这些事件加入与当前栈不同的另一个队列,也就是事件队列,而这个队列也不会立刻执行其回调,而是当前栈中所有任务执行完成之后,主线程处于空闲状态,此时查找当前队列中是否有任务,如果有,主线程会从中取出第一个事件,并将该事件对应的回调放到执行栈中,执行当前栈的同步代码,然后是第二个,然后是第三个,如此反复就成了一个事件循环

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function sum (a, b) {
return console.log(a + b)
}

sum(0, 1)
sum(1, 1)
setTimeout(function () {
sum(2, 1)
}, 51)
setTimeout(function () {
sum(3, 1)
}, 50)
sum(4, 1)
sum(5, 1)

// 1
// 2
// 5
// 6
// 3
// 4

这段代码就可以验证事件循环的实现方式,首先,会执行同步代码异步代码会被挂起,不会执行,自上而下也就是当前栈中的所有任务执行完成之后,也就是sum(5, 1),此时打印出6,这时,主流程代码执行完毕,处于闲置状态,这时,之前被挂起的异步任务队列会被执行,从第一个开始,setTimeout(function () {}, 50) 优先返回结果,会被放到第一个队列,同步代码执行完毕之后,异步队列的回调会被放入栈中执行同步代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
setTimeout(() => {
console.log('1')
}, 0)

new Promise((resolve) => {
console.log('2');
resolve()
}).then(() => {
console.log('3')
new Promise((resolve) => {
console.log('resolve');
resolve()
}).then(() => {
console.log('end resolve');
})
})

console.log(4)

这里涉及到宏任务,微任务的概念
宏任务:setTimeOut、setInverter、JavaScript代码
宏任务:MutationObserver,Promise的回调函数
事件循环的顺序决定js代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。
上面代码的执行顺序

  • 代码为宏任务在主线程,先会遇到 setTimeout 将它的回调函数分发到宏任务的事件队列中
  • 走到new Promise的时候代码会直接执行 会先执行console.log('2'), 然后将 then 的回调函数执行的方法发放的微任务的事件队列中
  • 之后执行同步代码的 console.log(4)
  • 主线程代码执行完成之后,先执行微任务中的代码,也就是then之后的回调代码 console.log('3')
  • 此时new Promise也会被执行,会执行console.log('resolve'), then作为微任务再次被放单事件队列中
  • 第一个pormise的回调执行完成之后,当前的任务队列中没有宏任务要执行,则开始微任务代码的执行也就是 then 的回调 console.log('end resolve');
  • 该队列下的所有宏任务,微任务执行完成之后结束此次任务,执行后续的任务
  • 微任务代码执行完成之后,会再次查找宏任务中的代码,也就是settimeout的回调函数 console.log('1')

结果

1
2
3
4
5
6
// 2
// 4
// 3
// resolve
// end resolve
// 1

事件循环示意图

event-loop

以上是浏览器中js的事件循环机制