现在每次文章开篇必前言,今天中午公司前端组发了一道题,当时面试也看过,没总结,导致看到题目知道是js执行顺序,并且包括宏任务,微任务,但是答案确差了很远,因此再次总结一下
我们知道,js是一条流水线,没有多余的分支,而且是从上往下执行。
原理是v8引擎是用来解析js的,因为v8引擎是单线程所以导致js也是单线程的,
此时你会有疑问:但是我们所认识的定时器不是“多线程”吗?实际上定时器是假的‘多线程’。 这个问题留到后面你就知道了
在v8引擎中有主线程和任务队列两个,同时执行顺利就是先同后异,先微后宏
当v8引擎解析到异步代码时,比如定时器,就会把异步代码交给相关的模块处理,处理完之后,再交给事件队列中排队,当执行栈有空时,消息队列就把事件交给调用栈执行。
简单理解就是:军人优先
比如在一个打仗的队伍里,有军人和普通人相互穿插着,军人就是同步,普通人就是异步。因为军人优先,所以所有军人会从队伍里出来,排成一队,则这就是主线程,普通人排成一队,则这就是任务队列,当军人依次完成任务不见了,主线程就会空出来,此时普通人中的成年男人(微任务)就站出来,排到主线程中,当成年男人完成任务也依次不见了,那么剩下的人(宏任务)就要占到主线程中。
通过这个故事,估计我们大体可以了解什么是同步,异步,微任务,宏任务,主线程和任务队列。下面通过做题可以加深对js执行顺序有更深的了解。
在做题前,我们先了解一下宏任务,微任务有哪些
-我所了解的微任务有
注意:Promise的内容是同步的,他的then catch finally的回调才是异步的
-我所了解的宏任务有
setTimeout(() => console.log(1), 0)
new Promise(res => {
console.log(2)
setTimeout(() => console.log(3), 0)
res()
}).then(() => console.log(4))
console.log(5)
练习1解析:这个代码是混着的,因此我们从上往下先把军人,男人和剩下的人分开出来
同步 | 微任务 | 宏任务 |
---|---|---|
console.log(2) | console.log(4) | setTimeout(() => console.log(1), 0) |
console.log(5) | setTimeout(() => console.log(3), 0) |
因此我们得出执行顺序就是2.5.4.1.3
setTimeout(() =>
new Promise(res => {
console.log(6)
setTimeout(() => console.log(7), 0)
res()
}).then(() => console.log(8))
, 0)
new Promise(res => {
console.log(2)
setTimeout(() => console.log(3), 0)
res()
}).then(() => console.log(4))
console.log(5)
练习2解析:
同步 | 微任务 | 宏任务 |
---|---|---|
console.log(2) | console.log(4) | setTimeout(() => new Promise(res => {console.log(6) setTimeout(()=> console.log(7), 0) res()}).then(() => console.log(8)), 0) |
console.log(5) | setTimeout(() => console.log(3), 0) |
我们先执行同步:则前三个我们很容易得出答案:2.5.4
此时我们对于宏任务包含微任务会很懵逼
此时在主线程只剩下宏任务,那么我们把宏任务再接着拆解
同步 | 微任务 | 宏任务 | |
---|---|---|---|
console.log(6) | console.log(8) | setTimeout(() => console.log(3), 0) | |
- | - | setTimeout(() => console.log(7), 0) |
在这个排队中,我们会觉得3为什么会在7前面,你可以只样子理解,当我们执行到宏任务a1的时候,此时他已经从任务队列出来了,到了主线程中,但是执行的时候,发现他们是一家人,里面有同,微,宏三种,因此我们要把他们拆开,但是你拆开宏就要乖乖排到后面,而不能插队
那么我们很自然的得出答案就是2.5.4.6.8.3.7
console.log("游戏开始",1);
new Promise( (resolve) => {
console.log("promise",2);
resolve();
})
.then( () => {
console.log("then",3);
});
console.log("promise结束",4);
setTimeout( () => {
console.log("setTimeout",5);
new Promise( (resolve) => {
console.log("promise",6);
resolve();
})
.then( () => {
console.log("then",7);
});
},0);
new Promise( (resolve) => {
console.log(promise,8);
resolve()
})
.then( () => {
console.log(then,9)
setTimeout( () => {
console.log("setTimeout",10);
},0);
})
console.log("游戏结束",11);
同理,我么接着拆分
同步 | 微任务 | 宏任务 |
---|---|---|
console.log(“游戏开始”,1); | console.log(“then”,3); | console.log(“setTimeout”,5);new Promise( (resolve) => {console.log(“promise”,6);resolve();}) .then( () => {console.log(“then”,7);}); |
console.log(“promise”,2); | console.log(then,9) setTimeout( () => {console.log(“setTimeout”,10); },0); | ——- |
console.log(“promise结束”,4); | ——- | ——- |
console.log(promise,8); | ——- | ——- |
console.log(“游戏结束”,11); | ——- | ——- |
我们可以得出前6个答案:1,2,4,8,11,3
此时我们执行到了
console.log(then,9)
setTimeout( () => {
console.log("setTimeout",10);
},0);
接着拆分
同步 | 微任务 | 宏任务 |
---|---|---|
console.log(then,9) | - | console.log(“setTimeout”,5);new Promise( (resolve) => {console.log(“promise”,6);resolve();}) .then( () => {console.log(“then”,7);}); |
- | - | setTimeout( () => { console.log(“setTimeout”,10);},0); |
同习题2一样,新增的宏任务不能插队
得出接下里答案是 9
此时我们接着拆分宏a1
同步 | 微任务 | 宏任务 |
---|---|---|
console.log(“setTimeout”,5); | console.log(“then”,7) | - |
console.log(“promise”,6); | - | setTimeout( () => { console.log(“setTimeout”,10);},0); |
得出答案5.6.710
最后习题3的答案就是1.2.4.8.11.3.9.5.6.7.10
做完题是不是觉得定时器是假的‘多线程’。