前言
提示:我们都知道js是一个单线程,里面的的请求方式分为两种,一种是同步请求,一种是异步请求,同步方法先执行完毕,然后再去异步任务队列中查看有没有异步任务,有才会执行。
1.相关示例
关于异步任务呢,又区分为两种【宏任务、微任务】,接下来让我们详细的了解两种的执行顺序与区别。
代码如下(示例):
<script>
console.log('1')
setTimeout(function () {
console.log('2')
});
new Promise(function (resolve) {
console.log('3');
resolve();
}).then(function () {
console.log('4')
setTimeout(function () {
console.log('5')
});
});
new Promise(function (resolve) {
console.log('6');
resolve();
}).then(function () {
console.log('7')
setTimeout(function () {
console.log('8')
});
});
</script>
2.代码解析
关于上面这段代码的执行顺序及最后的日志打印结果,会让好多不熟悉【宏任务、微任务】执行顺序的小伙伴看的一脸懵逼,我第一次看到觉得最后的打印结果应该是1, 2, 3, 4, 6, 7, 5, 8,看到最后的输出结果,才开始正视这个问题,看了许多相关案例,才了解一些皮毛
解析之前我们首先要知道常见的‘宏任务’和‘微任务’有哪些,
常见的微任务有:process.nextTick、Promise和 MutationObserver(监听DOM变化的事件)
常见的宏任务有:setTimeout、setInterval、setImmediate、 script标签中包含整体的代码块、 I/O操作、 UI渲染等。
关于两者之间的区别:微任务是批量执行、宏任务则是一个一个的执行。
了解完这些我们就可以上面的代码了
这里我们可以看到,script标签包含的下面的代码块,那么这就属于是第一个异步任务,也就是宏任务,首先去执行它
<script>
// 这里我们都可以看到,宏任务第一次执行,第一次的日志输出(1),
//这里相信大家都能看懂,接下来我们往下面看
console.log('1')
//执行到这里的时候,有的同学可能会问了,不是应该正常输出(2)吗?其实不然,我们知道,
//第一次执行完上面的宏任务了,此时呢,会先去异步任务队列中查找有没有需要执行的微任务,所以此时先跳过它,我们接着往下走
setTimeout(function () {
console.log('2')
});
//我们通过上面了解到Promise是属于微任务中的,所以这里会执行第一个Promise对象
new Promise(function (resolve) {
//执行new Promise,正式执行第二次打印输出(3)
console.log('3');
//这里resolve()执行,改变了promise对象的状态
resolve();
//那么这里会正常执行第三次输出吗?其实不是,promise执行后,改变执行状态(成功 or 失败)后,
//我们可以通过.then等相关方法获取到promise对象的执行结果,所以这里直接将一整块的代码块,
//同时丢入微任务队列中,其中也包含setTimeout,继续往下看
}).then(function () {
console.log('4')
setTimeout(function () {
console.log('5')
});
});
//又一个promise对象,不用说了,属于微任务
new Promise(function (resolve) {
//执行new Promise,正式执行第三次打印输出(6)
console.log('6');
//这里和上面一样resolve()执行,改变了promise对象的状态
resolve();
//这里不用看了,.then方法监听promise对象的执行结果,属于是微任务,将一整块代码块,
//包含setTimeout一起丢进微任务队列中,那么接下来你会问了,看代码书写顺序,微任务执行完了,应该执行宏任务了吧?
//其实不对,我们想一下,第一个promise对象是不是执行完毕,.then的时候将一些代码块丢进微任务队列中了?
//是不是应该把微任务队列中的任务也执行呢?是的...就是这样...我们往回看,第一次.then()监听的地方
}).then(function () {
console.log('7')
setTimeout(function () {
console.log('8')
});
});
</script>
<script>
console.log('1')
setTimeout(function () {
console.log('2')
});
new Promise(function (resolve) {
console.log('3');
resolve();
//看这里,看这里,第二个promise对象已经执行了,正常输出了第三次打印(6),接下来我们上面说了,
//此时还需要查看微任务队列中有没有需要执行的微任务,这时我们就发现了,第一个promise对象的.then()监听时,
//将这一整块代码都丢进微任务队列中了,这时我们需要做的就是执行这一块代码。
}).then(function () {
// 监听第一次promise的执行结果,正式输出第四次打印(4)
console.log('4')
//到这里有同学会问了,这肯定该执行下面的输出了把?别着急,往下看
setTimeout(function () {
console.log('5')
});
});
new Promise(function (resolve) {
console.log('6');
resolve();
//看这里,这里的.then()也是属于微任务队列中的,我们还需要执行它的
}).then(function () {
// 监听第二次promise的执行结果,正式输出第五次打印(7),此时整个微任务队列任务都执行完成了,
//此时我们要做的就是查看异步队列中有没有需要执行的宏任务,按照代码的执行顺序,我们往上翻
console.log('7')
setTimeout(function () {
console.log('8')
});
});
</script>
<script>
console.log('1')
//找到了,setTimeout属于是宏任务中的一种
setTimeout(function () {
//正式执行第六次打印,日志输出(2),接下来要做什么呢?宏任务执行完了,
//现在我们需要做的是去查看异步任务队列中,有没有需要执行的微任务,当前代码块并没有微任务,继续往下找
console.log('2')
//代码整个的执行了,发现在setTimeout中并没有需要执行的微任务
});
new Promise(function (resolve) {
console.log('3');
resolve();
}).then(function () {
console.log('4')
// 没有需要执行的微任务,此时我们再去执行这个宏任务
setTimeout(function () {
// 正式输出第七次打印,日志输出(5)
console.log('5')
// 宏任务执行完成了,我们需要做的还是去查看微任务队列中有没有需要执行的微任务,如果有,
//批量执行,当前代码块并没有
});
});
new Promise(function (resolve) {
console.log('6');
resolve();
}).then(function () {
console.log('7')
//执行到这里了,还是没有微任务,那就执行这个宏任务
setTimeout(function () {
//正式输出第八次打印,输出日志(8)
console.log('8')
//执行完宏任务了,再次查看有没有需要执行的微任务,发现此时还是没有微任务,
//那就找一下有没有宏任务吧,此时整个异步队列中,宏任务、微任务全部执行完成了
});
});
</script>
此时我们打开浏览器,看一下控制台,最后的打印结果为: 1、3、6、4、7、2、5、8