事件循环
事件循环其实就是浏览器解析js代码的一种运行机制或者一种执行规则。
我认为的事件循环就是事件队列,就好像js代码可以分为同步代码以及异步代码(微任务、宏任务),而浏览器执行这些js代码的先后顺序就叫做事件循环。
异步任务主要有:
事件、网络请求(ajax)、定时器、promise.then()、async/await
宏任务:
事件、网络请求(ajax)、定时器
微任务:
promise.then()、async/await
事件循环执行机制
1 首先进入到script标签中,就先当于进入了第一次的事件循环
2 遇到同步代码,立即执行
3 遇到宏/微任务,把它们放入到宏/微任务队列中
4 执行完毕所有的同步代码
5 执行微任务代码
6 完成所有的微任务代码,执行宏任务代码,直至所有的宏任务队列清空
注意:
1 宏/微任务的内部执行顺序与上面的六步完全一样
2 如果多个script标签,它的执行权重应该是在微任务的后面,宏任务的前面
以下用代码进行解释:
第一题:
<script>
console.log(1)
setTimeout(function () {
console.log(2)
}, 0)
const p = new Promise((resolve, reject) => {
resolve(1000)
})
p.then(data => {
console.log(data)
})
console.log(3)
</script>
执行结果:1 3 1000 2
解析:1和3为同步,直接输出;2为宏任务;1000为微任务
第二题:
<script>
console.log(1)
setTimeout(function () {
console.log(2)
new Promise(function (resolve) {
console.log(3)
resolve()
}).then(function () {
console.log(4)
})
})
new Promise(function (resolve) {
console.log(5)
resolve()
}).then(function () {
console.log(6)
})
setTimeout(function () {
console.log(7)
new Promise(function (resolve) {
console.log(8)
resolve()
}).then(function () {
console.log(9)
})
})
console.log(10)
</script>
执行结果:1 5 10 6 2 3 4 7 8 9
解析:
第一步:1 5 10为同步,直接输出;还有三个函数分别为定时器、promise、定时器
第二步:执行promise,输出6(promise中只有.then才是异步,promise仅仅是容器,里面的代码执行与否取决于代码本身)
第三步:执行第一个定时器;里面2 3为同步,4为异步,按序输出
第四步:第二个定时器与上面一致
第三题:
<script>
new Promise((resolve, reject) => {
resolve(1)
new Promise((resolve, reject) => {
resolve(2)
}).then(data => {
console.log(data)
})
}).then(data => {
console.log(data)
})
console.log(3)
</script>
执行结果:3 2 1
解析: 3是同步;promise内部代码执行完毕才会调用then,所以先输出2 然后是1
第四题:
<script>
setTimeout(() => {
console.log(1)
}, 0)
new Promise((resolve, reject) => {
console.log(2)
resolve('p1')
new Promise((resolve, reject) => {
console.log(3)
setTimeout(() => {
resolve('setTimeout2'); //'混淆你的'
console.log(4)
}, 0)
resolve('p2')
}).then(data => {
console.log(data); //'p2'
})
setTimeout(() => {
resolve('setTimeout1'); //'混淆你的'
console.log(5)
}, 0)
}).then(data => {
console.log(data); //'p1'
})
console.log(6)
</script>
执行结果:2 3 6 p2 p1 1 4 5
解析:2 3 6是同步,定时器函数最后执行,所以先执行p2 p1,然后定时器按先后顺序执行
第五题:
<script>
console.log(1);
async function fnOne() {
console.log(2);
await fnTwo(); // 右结合先执行右侧的代码, 然后等待
console.log(3);
}
async function fnTwo() {
console.log(4);
}
fnOne();
setTimeout(() => {
console.log(5);
}, 2000);
let p = new Promise((resolve, reject) => { // new Promise()里的函数体会马上执行所有代码
console.log(6);
resolve();
console.log(7);
})
setTimeout(() => {
console.log(8)
}, 0)
p.then(() => {
console.log(9);
})
console.log(10);
</script>
<script>
console.log(11);
setTimeout(() => {
console.log(12);
let p = new Promise((resolve) => {
resolve(13);
})
p.then(res => {
console.log(res);
})
console.log(15);
}, 0)
console.log(14);
</script>
执行结果:1 2 4 6 7 10 3 9 11 14 8 12 15 13 5
解析:有两个script标签;先看第一个,1 2 4 6 7 10为同步,async里面只有await是异步的,不过阻隔的不是await后面的函数,而是await下面的代码,然后输出3,再执行promise,上面的script剩下两个定时器了,最后执行,执行下面的script,输出11 14,再依次执行定时器。