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

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

作者: 虎牙工务员刘波 | 来源:发表于2021-05-10 18:25 被阅读0次

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

    • js代码分为同步与异步

    • 宏任务与微任务都是异步

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


    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宏任务、微任务的正确执行顺序,需要注意的是微任务的执行机制。

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

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/rzgnrltx.html