JavaScript 事件循环

JavaScript 事件循环

唠唠基础😊

众所周知 js 是单线程的,至于为什么是单线程的?

答: js作为脚本语言,其主要负责 操作DOM用户交互 。假如变成多线程的话,一个线程在给某个DOM 添加内容 ,而另外一个线程正在删除这个 DOM 一切就乱套了。

01 同步任务和异步任务

虽然 js 是单线程,但是引入两个概念,一个是同步,另外一个异步

  • 同步任务:

代码是按照执行栈顺序执行下来的,比如:

1
2
3
4
5
console.log("Hello")
console.log("DouR")
// console:
// 1:Hello
// 2:DouR
  • 异步任务:

异步操作,比如:setTimeoutPromiseAjax…等。当顺序执行 执行栈 代码时,遇到异步操作,可以把它们看做比较耗时的工作,放入任务队列中去,接着执行代码,等执行完同步代码后在执行任务队列 的任务。

1
2
3
4
5
6
7
setTimeout(()=>{
console.log("Hello")
})
console.log("DouR")
// console:
// 1: DouR
// 2: Hello

可以很清晰看到先打印 DouR

02 Event Loop

事件循环就是 入栈出栈 的过程,上面提到的异步任务,可以分成 宏任务(Macrotask)微任务(Microtask)

宏任务:setTimeout, setInterval, setImmediate, I/O, UI render

微任务:process.nextTick, Promises, Object.observe, MutationObserver(H5 新特性)

每一次 Event Loop 触发时:

  1. 首先执行主执行栈任务,例如 script 任务
  2. 分别将 宏任务微任务 ,压入宏任务队列微任务队列
  3. 接着,先取出 微任务队列 执行直至清空(执行过程中,碰到微任务或者宏任务 分别压入队列中去,用于下一轮 Event loop执行)
  4. —— 新的一轮 Event Loop——
  5. 取出 宏任务队列 执行直至清空
  6. 取出 微任务队列 执行直至清空
  7. 5、6重复

试试这个栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
console.log('global')

for (var i = 1;i <= 5;i ++) {
setTimeout(function() {
console.log(i)
},i*1000)
console.log(i)
}

new Promise(function (resolve) {
console.log('promise1')
resolve()
}).then(function () {
console.log('then1')
})

setTimeout(function () {
console.log('timeout2')
new Promise(function (resolve) {
console.log('timeout2_promise')
resolve()
}).then(function () {
console.log('timeout2_then')
})
}, 1000)

结果:

image-20220308213843024

说个冷知识,浏览器的事件循环 和 Node事件循环不一致浏览器和Node不同的事件循环(Event Loop) - 掘金 (juejin.cn)