天下之事常成于困约,而败于奢靡。这篇文章主要讲述??一起谈一谈js中的宏任务和微任务!??相关的知识,希望能为你提供帮助。
前面面试的文章中我们说过一道关于宏任务和微任务的题:
setTimeout(function(){
console.log(\'1\')
});
new Promise(function(resolve){
console.log(\'2\');
resolve();
}).then(function(){
console.log(\'3\')
});
console.log(\'4\')
试问一下上面代码的执行顺序是啥?
But,事实真是如此吗?我有点慌,于是我粘贴到浏览器去瞅两眼:
文章图片
我擦嘞,居然是2,4,3,1!我简直不敢相信!
文章图片
带着困惑的我,只能去好好研究研究javascript的运行机制了!
1、关于javaScript
JavaScript是一门单线程语言,即一次只能完成一个任务,若有多个任务要执行,则必须排队按照队列来执行(前一个任务完成,再执行下一个任务)。
2、JavaScript事件循环
既然js是单线程,那就像只有一个窗口的食堂,学生需要排队一个一个打饭,同理js任务也要一个一个顺序执行。这种模式执行简单,但随着日后的需求,事务,请求增多,这种单线程模式执行效率必定低下。只要有一个任务执行消耗了很长时间,在这个时间里后面的任务无法执行。
常见的有新闻包含的超清图片加载很慢,难道我们的网页要一直卡着直到图片完全显示出来?为了解决这个问题,JavaScript语言将任务执行模式分成同步和异步:
同步模式: 就是上面所说的一种执行模式,后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的。
异步模式: 就是每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。
文章图片
导图要表达的内容用文字来表述的话:
- 同步和异步任务分别进入不同的执行" 场所" ,同步的进入主线程,异步的进入Event Table并注册函数。
- 当指定的事情完成时,Event Table会将这个函数移入Event Queue。
- 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
- 上述过程会不断重复,也就是常说的Event Loop(事件循环)。
let data = [];
$.ajax({
url:blog.csdn.net,
data:data,
success:() =>
{
console.log(\'发送成功!\');
}
})
console.log(\'代码执行结束\');
上面是一段简易的
ajax
请求代码:- ajax进入Event Table,注册回调函数
success
。 - 执行
console.log(\'代码执行结束\')
。 - ajax事件完成,回调函数
success
进入Event Queue。 - 主线程从Event Queue读取回调函数
success
并执行。
【??一起谈一谈js中的宏任务和微任务!??】然而实际上,异步队列里是还有门道的,我们那道面试题,setTimeout和promise的.then()都在异步队列了!接下来,讲讲那些门道(宏任务和微任务)。
3、宏任务和微任务
每个人的理解方式不同,因为宏任务和微任务并不是标准,但执行的顺序在js中是大一统了的。
宏任务
# | 浏览器 | Node |
< script> 整体代码 | √ | √ |
setTimeout | √ | √ |
setInterval | √ | √ |
setImmediate | x | √ |
requestAnimationFrame | √ | x |
Ajax | √ | √ |
DOM事件 | √ | √ |
# | 浏览器 | Node |
process.nextTick | x | √ |
MutationObserver | √ | x |
Promise.then catch finally | √ | √ |
//回调才是异步任务
setTimeout(function(){//宏任务
console.log(\'1\')
});
new Promise(function(resolve){
console.log(\'2\');
//同步主线程
resolve();
}).then(function(){//微任务
console.log(\'3\')
});
console.log(\'4\')//同步主线程
因此:2,4,3,1的结果就出来了!
除此我们来拓展一下:
setTimeout(() =>
{//宏任务队列1
console.log(\'1\');
//宏任务队1的任务1setTimeout(() =>
{//宏任务队列3(宏任务队列1中的宏任务)
console.log(\'2\')
}, 0)new Promise(resolve =>
{
resolve()
console.log(\'3\')//宏任务队列1的任务2
}).then(() =>
{//宏任务队列1中的微任务
console.log(\'4\')
})}, 0)setTimeout(() =>
{//宏任务队列2
console.log(\'5\')
}, 0)console.log(\'6\')//同步主线程
以上代码会怎么输出呢?
4、拓展宏任务微任务上面出了复杂的题,小伙伴们不妨可以想一想,这种复杂情况,一个套一个的该怎么执行呢?
文章图片
所以输出的结果是什么?是6,1,3,4,5,2!
文章图片
文章图片
?
经过验证,结果正确!
5、总结实际上你只需要知道拓展之前的,毕竟拓展后的确实比较复杂,需要一定的理解能力,他可以三层,可以四层等....我不信你会跟这题目一样写代码,这不是憨批是什么????。
对于宏任务和微任务请记住这几点:
- 微任务比宏任务执行要早。
- 宏任务里如果有宏任务,不会执行里面的那个宏任务,而是被丢进任务队列后面,所以会最后执行。
最后出个题:
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\')
})
})
这题里有process.nextTick,请在nodejs中验证哦~
推荐阅读
- 美信科技通过CMMI3级认证,研发能力获国际认可
- 怎么通过命令的形式列出各个linux系统已经安装的软件包()
- 带你全面的了解二叉树
- psm_job缺库,程序无法正常启动
- BPF BTF 详细介绍
- 9.文本处理工具
- Spring Cloud Gateway 没有链路信息,我 TM 人傻了(上)
- 1.docker概述及其历史
- 云计算奇妙学习之旅第四期(华为计算虚拟化精讲)