js事件循环简单理解,微任务与宏任务运行顺序(二)
看了一些文章,没有很清楚,去捋了一下🤦。现在搞懂了就写下来记录下,好记性不如烂笔头。
时隔半年多,我回来看这篇文章发现有许多不足之处,所以做了一些补充,增加了几个确切的概念理解:
-
js代码分为同步与异步
-
宏任务与微任务都是异步
关系如下(仅展示关系,不是完整运行流程):
![](https://img.haomeiwen.com/i24769903/cf2eb25142407ee4.png)
JavaScript的事件循环模永不阻塞
这句不是我说的,是mdn官网说的:MDN
下面是要理解的。
1、js是单线程,不像java是多线程,js运行任务同步会有阻塞问题,所以一定要有异步这个概念(异步分为:宏任务、微任务)。
2、event loop是js的事件循环机制,也就是微任务与宏任务靠这个机制来按顺序执行。
3、js的执行宿主是浏览器和node.js
4、不同的浏览器执行微任务、宏任务顺序可能略略略略略微不同(例如谷歌70.几版本上运行一道经典的面试题顺序和现在最新版是不一样的)。
同步任务总是先执行,遇到异步直接挂起(不阻塞)
异步分为:
宏任务:setTimeout、dom渲染、I/O
微任务:promise中的then()、await
其中:promise.then和async await
(两者运行的宿主环境即浏览器环境自带),
setTimeout
(js语言自带),I/O
(输入或者输出操作,你点击按钮、输入文字之类操作)
所以这是我这篇文章要将的重点。因为同步总是先执行,所以不必理会它的执行顺序,它总是先。所以重点是看宏任务和微任务的执行顺序(异步)
异步执行顺序的关键点:
1、从上往下,先执行整个模块的宏任务代码,遇到promise.then()、await微任务了,把微任务放到任务队列并标记为微任务。遇到宏任务的异步setTimeout,也把他放到放到任务队列里并标记为宏任务,等整体的script宏任务执行完毕,才去执行任务队列里的代码。规则:任务队列里的微任务先执行,且按照先进后执行的规则来执行。
2、promise虽说属于微任务,但是准确说.then( )才是会被归类到任务队列里的微任务。promise里的代码会按宏任务顺序执行下来,不会被放到任务队列里。
验证下:
new Promise((reslove)=>{
console.log(1)
}).then()
console.log(2)
// 1 2 按照正常代码执行顺序从上往下执行
如果把console.log(1)放到.then里面执行,我们来看下
new Promise((reslove)=>{
reslove() //需要reslove一个值.then才能执行
}).then( () => console.log(1))
console.log(2)
// 2 1
![](https://img.haomeiwen.com/i24769903/eca0c439acb8868b.png)
3、async也是如此,只有await下面的代码才属于微任务,会被归类到任务队列里。
有个注意点是:当代码碰到await时候,会先执行当前行的await右边代码,然后把await下面的所有代码放到微任务队列!请记住,await下面的都属于微任务!
验证下:
test =async ()=>{
await console.log(1) //会先执行完当前的这个await
console.log(3)
}
console.log(2)
test()
// 2 1 3
![](https://img.haomeiwen.com/i24769903/dc81b770f59256ca.png)
根据以上的3点,接下来看下下面的代码是如何任务执行顺序的:
test =async ()=>{
await console.log(1)
await console.log(3)
await console.log(4)
}
console.log(2)
new Promise((reslove)=>{
reslove()
}).then(()=>console.log(5)).then(()=>console.log(6))
test()
setTimeout(()=>console.log(7))
// 2 1 5 3 6 4 7
上面这个代码从上往下执行,先执行输出2,然后执行promise,遇到.then()之后会把.then(()=>console.log(5)).then(()=>console.log(6))整个放到任务队列里,作为微任务待执行。接着再往下走,到test(),执行第一个await,输出1,此时会把下面的所有代码放到任务队列里作为微任务待执行。所以此时任务队列就如下图所示。
![](https://img.haomeiwen.com/i24769903/8394f152d9566014.png)
此时的你可能会以为里面的微任务顺序 就是会 5 6 3 4 这个顺序执行的,但是实际上不是的!这里特别注意,是因为每次遇到await或者.then,会任务队列拿出这个代码模块,先执行完拿第一个微任务代码,然后再把后面的再放进任务队列里。
所以当执行任务队列的微任务顺序是:
1、.then(()=>console.log(5)).then(()=>console.log(6))整个代码模块拿出来,拿出来后先执行.then(()=>console.log(5)),输出5
2、然后碰到.then,就把.then(()=>console.log(6))放到任务队列的微任务的最后位置(所以此时他会在微任务的最后)
![](https://img.haomeiwen.com/i24769903/557f40979e2676ed.png)
3、然后执行
await console.log(3)
await console.log(4)
这个代码模块,整个代码模块拿出来,await console.log(3) 运行了,输出3,碰到await console.log(4),再把他放到任务队列里的微任务的最后位置
![](https://img.haomeiwen.com/i24769903/6e4b57104c8a5177.png)
4、然后再执行输出6 ,输出 4.最后执行任务队列里的宏任务,输出7
所以这就是js宏任务、微任务的正确执行顺序,需要注意的是微任务的执行机制。