JS的事件循环
了解js的事件循环前,先要明确的几个事情:
- JS分为同步任务和异步任务
- JS是单线程,其中同步任务在主线程上执行,形成一个执行栈,在主线程之外还存在着一个任务队列,若是异步任务有了运行结果,就会在任务队列之中放置一个事件
- 一旦同步任务执行完毕,就会读取任务队列,开始执行可运行的异步任务
根据JS规范,事件循环是通过任务队列机制进行协调的。在一个event loop
中,可以有一个或者多个任务队列,一个任务队列是一系列有序任务的集合。每个任务都有一个任务源(task
source),同一个任务源的task须放到同一个任务中。例如:setTimeout/Promise等等API就是任务源。
宏任务:每次执行栈执行的代码就是一个宏任务,包含:script(整体代码)、setTimeout、setInterval、I/O、UI交互事件、postMessage等。
微任务:可以理解是在当前 task 执行结束后立即执行的任务。也就是说,在当前task任务后,下一个task之前。如:Promise.then。
// 第一个宏任务
<script>
// 同步任务1
console.log(1)
// 微任务1
new Promise((resolve, reject) => {
resolve(8)
// 若此处有操作(同步任务,例如console.log())则执行在abc()之前
}).then(res => {
console.log(res)
})
function abc() {
console.log(2)
}
// 同步任务2
abc()
// 第二个宏任务
setTimeout(() => {
// 同步任务4
console.log(3)
// 微任务3
new Promise((resolve, reject) => {
resolve(7)
}).then(res => {
console.log(res)
})
// 同步任务5
console.log(4)
}, 0)
// 微任务2
new Promise((resolve, reject) => {
resolve(5)
}).then(res => {
console.log(res)
})
// 同步任务3
console.log(6)
// DOM渲染
document.querySelector('div').style.color = 'blue'
</script>
以上的宏任务,微任务,同步,异步已标出。
执行顺序为:
- console.log(1)
- abc()console.log(2)
- console.log(6)
- resolve(8)
- resolve(5)
- DOM渲染
- setTimeout里的 console.log(3)
- console.log(4)
- resolve(7)
async function a1 () {
console.log('a1 start')
await a2()
console.log('a1 end')
}
async function a2 () {
console.log('a2')
}
console.log('script start')
setTimeout(() => {
console.log('setTimeout')
}, 0)
Promise.resolve().then(() => {
console.log('promise1')
})
a1()
let promise2 = new Promise((resolve) => {
resolve('promise2.then')
console.log('promise2')
})
promise2.then((res) => {
console.log(res)
Promise.resolve().then(() => {
console.log('promise3')
})
})
console.log('script end')
这里的执行顺序为
1. console.log('script start')
2. a1()方法执行:console.log('a1 start') 执行a2()console.log('a2')
3. console.log('promise2')
4. console.log('script end')
5. console.log('promise1')
6. console.log('a1 end')
7. console.log(res) ('promise2.then')
8. console.log('promise3')
9. console.log('setTimeout')