js事件循环简单理解,微任务与宏任务运行顺序(二)

2021-05-10  本文已影响0人  虎牙工务员刘波

看了一些文章,没有很清楚,去捋了一下🤦。现在搞懂了就写下来记录下,好记性不如烂笔头。
时隔半年多,我回来看这篇文章发现有许多不足之处,所以做了一些补充,增加了几个确切的概念理解:

关系如下(仅展示关系,不是完整运行流程):


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   

3、async也是如此,只有await下面的代码才属于微任务,会被归类到任务队列里。
有个注意点是:当代码碰到await时候,会先执行当前行的await右边代码,然后把await下面的所有代码放到微任务队列!请记住,await下面的都属于微任务!
验证下:

 test =async ()=>{
  await console.log(1)   //会先执行完当前的这个await
  console.log(3)
}
console.log(2)
test()
//   2   1   3

根据以上的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,此时会把下面的所有代码放到任务队列里作为微任务待执行。所以此时任务队列就如下图所示。


此时的你可能会以为里面的微任务顺序 就是会 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))放到任务队列的微任务的最后位置(所以此时他会在微任务的最后)

3、然后执行
await console.log(3)
await console.log(4)
这个代码模块,整个代码模块拿出来,await console.log(3) 运行了,输出3,碰到await console.log(4),再把他放到任务队列里的微任务的最后位置

4、然后再执行输出6 ,输出 4.最后执行任务队列里的宏任务,输出7

所以这就是js宏任务、微任务的正确执行顺序,需要注意的是微任务的执行机制。

总结:执行宏任务模块代码,碰到微任务,放到任务队列,继续执行宏任务模块代码(直到没有了,才去执行任务队列里的任务)。任务队列里的任务,微任务永远先执行,才去执行任务队列里的宏任务。
上一篇下一篇

猜你喜欢

热点阅读