❤️一起谈一谈js中的宏任务和微任务!

简介: ❤️一起谈一谈js中的宏任务和微任务!

前面面试的文章中我们说过一道关于宏任务和微任务的题:

setTimeout(function(){
   console.log('1')
});
new Promise(function(resolve){
   console.log('2');
     resolve();
}).then(function(){
  console.log('3')
});
console.log('4')

试问一下上面代码的执行顺序是啥?


有小伙伴可能会答:2,4,1,3


估摸着是这么想的:我难道不知道js是一行一行执行的,setTimeout是异步,所以先放后面;往下走,执行了console.log(2),.then()是异步的,放在后面;走了console.log(4);再去异步队列里走,先是console.log(1);再是console.log(3)。


But,事实真是如此吗?我有点慌,于是我粘贴到浏览器去瞅两眼:

20210902214100776.png


我擦嘞,居然是2,4,3,1!我简直不敢相信!


20210902231730143.png


带着困惑的我,只能去好好研究研究JavaScript的运行机制了!


1、关于JavaScript

JavaScript是一门单线程语言,即一次只能完成一个任务,若有多个任务要执行,则必须排队按照队列来执行(前一个任务完成,再执行下一个任务)。

2、JavaScript事件循环

既然js是单线程,那就像只有一个窗口的食堂,学生需要排队一个一个打饭,同理js任务也要一个一个顺序执行。这种模式执行简单,但随着日后的需求,事务,请求增多,这种单线程模式执行效率必定低下。只要有一个任务执行消耗了很长时间,在这个时间里后面的任务无法执行。


常见的有新闻包含的超清图片加载很慢,难道我们的网页要一直卡着直到图片完全显示出来?为了解决这个问题,JavaScript语言将任务执行模式分成同步和异步:


同步模式:就是上面所说的一种执行模式,后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的。


异步模式:就是每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。

20210902224135435.png

导图要表达的内容用文字来表述的话:


同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入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的执行顺序有了初步了解。然而这也是为什么会有小伙伴回答2,4,1,3的原因。


然而实际上,异步队列里是还有门道的,我们那道面试题,setTimeout和promise的.then()都在异步队列了!接下来,讲讲那些门道(宏任务和微任务)。


3、宏任务和微任务

每个人的理解方式不同,因为宏任务和微任务并不是标准,但执行的顺序在js中是大一统了的。

宏任务

image.png

微任务

image.png


宏任务包括:<script>整体代码、setTimeout、setInterval、setImmediate、Ajax、DOM事件

微任务:process.nextTick、MutationObserver、Promise.then catch finally


process.nextTick差异太大,不同的node执行不统一,不做标准

**微任务比宏任务的执行时间要早**


Tip:有些人喜欢将<script>整体代码放在宏任务里,但我个人不喜欢,在我这里它只是第一执行的主线程,我个人是将宏任务和微任务都归类到异步任务里!


我们再来看看那道面试题:


//回调才是异步任务
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:异步中的宏任务,故第二

因此:2,4,3,1的结果就出来了!

除此我们来拓展一下:

setTimeout(() => {//宏任务队列1
  console.log('1');//宏任务队1的任务1
  setTimeout(() => {//宏任务队列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')//同步主线程

执行整体代码(宏任务)console.log('6') >> 宏任务队列1、宏任务队列2位异步(依次执行)


宏任务队列1:=>


       console.log('1')


       console.log('3')


       console.log('4')//宏任务中的微任务


       剩下的不会先执行,因为是宏任务中的宏任务(console.log(2)) ,要被继续丢进任务队列后  


宏任务队列2:=>


        console.log('5')


宏任务队列1中的宏任务3:=>


       console.log('2')


以上代码会怎么输出呢?


4、拓展宏任务微任务

上面出了复杂的题,小伙伴们不妨可以想一想,这种复杂情况,一个套一个的该怎么执行呢?

20201006174826470.png

整体代码:


6:第一个同步主线程,故第一

script整体代码里没有微任务故直接执行宏任务=>


宏任务队列:


宏任务队列1

       任务1:console.log(1)


       任务2:console.log(3)


       宏任务队列1中的微任务:console.log(4)


       宏任务队列3:因他是宏任务队列1中的宏任务,所以被丢进了任务队列最后,我们先看宏任务队列1同级的是否还有宏任务,有就先执行同级的,没有才可以执行宏任务队列3!故最后!


宏任务队列2  

       console.log(5)


所以输出的结果是什么?是6,1,3,4,5,2!

20210903003029307.png

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中验证哦~


相关文章
|
3月前
|
JavaScript 前端开发 API
详解队列在前端的应用,深剖JS中的事件循环Eventloop,再了解微任务和宏任务
该文章详细讲解了队列数据结构在前端开发中的应用,并深入探讨了JavaScript的事件循环机制,区分了宏任务和微任务的执行顺序及其对前端性能的影响。
|
2月前
|
JavaScript 前端开发 调度
在JavaScript中异步任务里的微任务和宏任务的特点和生命周期
在JavaScript中异步任务里的微任务和宏任务的特点和生命周期
45 0
|
3月前
|
前端开发 JavaScript API
JavaScript 的宏任务和微任务有什么区别
【9月更文挑战第6天】JavaScript 的宏任务和微任务有什么区别
91 4
|
4月前
|
存储 前端开发 JavaScript
JavaScript 并发任务控制
【8月更文挑战第31天】JavaScript 并发任务控制
52 2
|
26天前
|
JavaScript 前端开发
JavaScript中的原型 保姆级文章一文搞懂
本文详细解析了JavaScript中的原型概念,从构造函数、原型对象、`__proto__`属性、`constructor`属性到原型链,层层递进地解释了JavaScript如何通过原型实现继承机制。适合初学者深入理解JS面向对象编程的核心原理。
25 1
JavaScript中的原型 保姆级文章一文搞懂
|
5月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
102 2
|
22天前
JS+CSS3文章内容背景黑白切换源码
JS+CSS3文章内容背景黑白切换源码是一款基于JS+CSS3制作的简单网页文章文字内容背景颜色黑白切换效果。
17 0
|
5月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的小区物流配送系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的小区物流配送系统附带文章源码部署视频讲解等
142 4
|
5月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的宠物援助平台附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的宠物援助平台附带文章源码部署视频讲解等
85 4
|
5月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的宠物交易平台附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的宠物交易平台附带文章源码部署视频讲解等
78 4