主线程 | 任务1,任务2 |
微队列 | 微队列任务1, 微队列任务2 |
延时队列 | 延时队列任务1, 延时队列任务2 |
交互队列 | .... |
事件循环的工作原理
-
主线程执行同步任务:
- 主线程首先执行所有同步任务(即栈中的任务)。这些任务会按顺序执行,直到没有更多同步任务为止。
-
遇到微任务或宏任务:
- 在执行同步任务时,如果遇到异步任务(例如
Promise
、setTimeout
等),这些异步任务会被添加到相应的队列中。具体来说:- 微任务(Microtask):如
Promise.then()
、MutationObserver
等会被加入到微任务队列中。 - 宏任务(Macrotask):如
setTimeout
、setInterval
、I/O
操作等会被加入到宏任务队列中。
- 微任务(Microtask):如
- 在执行同步任务时,如果遇到异步任务(例如
-
执行微任务队列:
- 当主线程的同步任务执行完毕后,微任务队列会被立即执行,直到微任务队列为空。微任务的优先级高于宏任务,所以它们会在每个事件循环中尽可能多地执行。
- 如果微任务中又生成了新的微任务,这些新的微任务会在当前事件循环中继续执行,直到微任务队列清空。
-
执行宏任务队列(延时队列和交互队列):
- 当微任务队列为空时,主线程会从宏任务队列中取出任务执行。宏任务队列中的任务是按照加入的顺序依次执行的。
- 延时队列:由
setTimeout
、setInterval
等产生的任务。 - 交互队列:用户交互事件(如
click
、scroll
等)会被放入交互队列。
- 延时队列:由
- 在执行宏任务时,如果遇到新的微任务,它们会被添加到微任务队列中,待下一轮微任务执行时处理。
- 当微任务队列为空时,主线程会从宏任务队列中取出任务执行。宏任务队列中的任务是按照加入的顺序依次执行的。
-
重复事件循环:
- 事件循环会不断重复,先执行主线程中的同步任务,然后处理微任务队列,接着处理宏任务队列。每次事件循环都会清空微任务队列后才会执行宏任务。
事件循环的顺序总结
- 执行主线程中的同步任务。
- 执行微任务队列中的任务(微任务优先级高于宏任务)。
- 执行宏任务队列中的任务(按照加入顺序)。
- 在宏任务执行过程中,如果遇到新的微任务,它们会被添加到微任务队列中,等微任务队列为空时再执行。
- 继续下一轮事件循环,重复上述过程。
练习:
console.log(1)setTimeout(() => {console.log(2)
}, 0)const promise = new Promise((resolve, reject) => {console.log(3)setTimeout(() => {new Promise((resolve, reject) => {resolve('success')console.log(8)}).then(data => {console.log(10)})console.log(4)}, 0)resolve('success')
})setTimeout(() => {console.log(5)
}, 0)console.log(6)promise.then(data => {console.log(7)
})console.log(9)
答案:
1
3
6
9
7
2
8
4
10
5