通过一道常见的面试题来展开今天的内容介绍吧。
setTimeout(() => {
console.log('set1')
new Promise((resolve, reject) => {
console.log('pro1');
resolve()
}).then(() => {
console.log('then1')
})
});
new Promise((resolve, reject) => {
console.log('pr2');
resolve();
}).then(() => {
console.log('then2');
setTimeout(() => {
console.log('set2');
})
});
setTimeout(() => {
console.log('set3')
})
console.log(4)
看到上面这个题,相信大家在面试中会经常碰到,也是许多面试者害怕和容易采坑的面试题,今天我们就来聊一聊关于这类题的解题原理和思路吧。
JS运行机制
大家都知道,JS的运行机制是从上到下,逐行代码解析运行的。通过下面一张图来解释一下代码的运行机制。
先运行同步队列任务,再运行异步队列任务,异步队列中有微任务,则先执行微任务,然后再执行宏任务,执行宏任务,则会先判断异步队列是否有微任务
异步任务队列运行机制
认识微任务和宏任务
- 常见的宏任务 setTimeout、setInterval
- 常见的微任务 process.nextTick、promise.then、catch、finally
牛刀小试
setTimeout(() => {
//宏任务
console.log('内层宏事件3')
}, 0)
console.log('外层宏事件1');
new Promise((resolve) => {
console.log('外层宏事件2');
resolve()
}).then(() => {
// 微任务
console.log('微事件1');
}).then(()=>{
// 微任务
console.log('微事件2')
})
解析过程
第一次循环
当代码开始执行的时候,异步任务的微任务和宏任务队列都是空,我们用空数组来表示
当代码碰到 setTimeout 的时候,会把 “内层宏事件3” 放到宏任务队列,此时的宏任务队列是[“内层宏事件3”]
当代码碰到 console.log 的时候,会直接打印出 “外层宏事件1”
当代码碰到 promise 的时候,会直接打印出“外层宏事件2”
当代码碰到 then 的时候,会把“微事件1”放到微任务队列,此时的微任务队列是[“微事件1”]
当再代码碰到 then 的时候,会把“微事件2”放到微任务队列,此时的微任务队列是[“微事件1”, “微事件2”]
第二次循环**
开始执行异步队列的任务,异步队列中包含微任务队列的事件,会直接打印出“微事件1”,此时的微任务队列是[“微事件2”]
当执行完“微事件1”后,会检测当前任务的是否有同步任务,为空时再执行下一个微任务事件
此时同步任务为空,直接打印出“微事件2”,然后再检测是否有同步任务
此时的同步任务和微任务都为空,则实行宏任务,会直接打印出“内层宏事件3”
所以,最终的打印顺序是:外层宏事件1 -> 外层宏事件2 -> 微事件1 -> 微事件2 -> 内层宏事件3
了解了js的运行机制后,当我们再碰这类型的面试题的手,就会轻松许多
给大家留两道面试题吧,练练手
面试题一
setTimeout(() => {
console.log('set1')
new Promise((resolve, reject) => {
console.log('pro1');
resolve()
}).then(() => {
console.log('then1')
})
});
new Promise((resolve, reject) => {
console.log('pr2');
resolve();
}).then(() => {
console.log('then2');
setTimeout(() => {
console.log('set2');
})
});
setTimeout(() => {
console.log('set3')
})
console.log(4)
面试题二
console.log('1');
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})