夜仿佛纸浸了油变成半透明体;它给太阳拥抱住了,分不出身来,也许是给太阳陶醉了,所以夕照晚霞褪后的夜色也带着酡红。 ---《围城》
老规矩,上猫。
我想,学习js的第一课,老师大概就会一遍遍强调:js是单线程的,js是单线程的,js是单线程的。
单线程:说的通俗一点,就是代码在执行过程中,另一段代码想要执行就必须等当前代码执行完成后才可以进行。
假设,JS是多线程的。试想一种情况,多个线程同时修改一个DOM,哪个线程又不是正经线程呢,这时候,浏览器该如何选择?所以,这个时候,就会牵扯到一个问题,既然他是单线程的,有些事件可能会循环多次或者延迟执行,遇到这些事件的时候,总不能就一直在那儿等着。
- JS代码执行过程中,同步任务直接执行,同步任务执行接受之后进行异步任务的执行。
- 异步任务分类为微任务和宏任务
- 宏任务:由宿主环境提供的任务---setTimeout/setInterval
- 微任务:由语言标准提供的任务----Promise/process.nextTick
事件循环:异步任务中,先执行微任务,再执行宏任务,在执行宏任务时,有可能产生新的任务,所以,宏任务执行结束后,会再去执行微任务,直到所有的异步任务执行结束。
废话不多说,直接带你一道题全面理解。
(function(){
setTimeout(() => {
console.log(0);
});
new Promise(resolve => {
console.log(1);
setTimeout(() => {
resolve();
Promise.resolve().then(() => console.log(2));
console.log(3);
});
Promise.resolve().then(() => console.log(4));
}).then(() => {
console.log(5);
Promise.resolve().then(() => console.log(6));
setTimeout(() => console.log(7));
});
console.log(8);
})()
让我们来逐步解析这道题目
补充:
promise声明过程是同步的, then() 是异步的,只有在执行promise.resolve()后才会触发。
由此可见这道题中的任务5只有在任务3中的resolve()执行的时候才会触发。
--第一次循环--
| 同步 | 异步(微任务 | 异步(宏任务)) |
|---|---|---|
| 任务2 | 任务4 | 任务1 |
| 任务6 | 任务3 | |
按照先同步后异步,先微任务后宏任务的顺序执行。
- 任务2 --------> 打印1
- 任务6---------> 打印8
- 任务4 --------> 打印4
--第二次循环--
第二轮循环从宏任务队列开始
- 任务1 --------> 打印0
- 任务3 --------> 先将任务5放入微任务队列,再将任务7放入微任务队列,最后 打印3
| 同步 | 异步(微任务) | 异步(宏任务) |
|---|---|---|
| 任务5 | ||
| 任务7 |
- 任务5 ------> 先打印5,将任务8扔进微任务,任务9扔进宏任务队列
- 任务7 ------> 打印2
--第三次循环--
| 同步 | 异步(微任务) | 异步(宏任务) |
|---|---|---|
| 任务8 | 任务9 |
- 任务8 ----> 打印6
- 任务9 -----> 打印7
最终打印的结果为:1,8,4,0,3,5,2,6,7
我们可以看到,控制台的执行结果中,多了一个“undefined”。这是因为,在第一轮循环结束后,控制台会输出自执行函数的执行结果,因为函数中没有设置返回值,默认返回"undefined""
以上
特别鸣谢:顾陇童小虎