zoukankan      html  css  js  c++  java
  • js 事件循环消息队列和微任务宏任务

    事件循环与消息队列

    因为js是单线程脚本语言,一般情况下代码是同步执行。也就是说js执行代码是一行一行向下执行的,前面没有执行完成是不会执行后面的代码的。

    • 同步和异步的区别其实就在于需不需要排队的问题
      • 同步:所有任务一视同仁,都得排队,先来后到;
      • 异步:可以按照一定规则(不至于乱套)插队执行;
    • 事件循环和消息队列怎么理解
      • 事件循环:单线程脚本语言javascript处理任务的一种执行机制,通过循环来执行任务队列里的任务。这个执行过程形象的称之为事件循环
      • 消息队列:js为单线程脚本语言,执行任务时需要排队,每当有新的任务来临时就加到这个队列后面。这个队列就叫消息队列或者任务队列

    浏览器与Node的事件循环有何区别?

    • 浏览器事件循环过程
      当某个宏任务执行完后,会查看是否有微任务队列。
      如果有,先执行微任务队列中的所有任务,
      如果没有,会读取宏任务队列中排在最前的任务,执行宏任务的过程中,遇到微任务,依次加入微任务队列。
      栈空后,再次读取微任务队列里的任务,依次类推。
    
    • node事件循环过程
    1.外部输入数据
    2.轮询阶段(poll)
    3.检查阶段(check)
    3.关闭事件回调阶段(close callback)
    4.定时器检测阶段(timer)
    5.I/O事件回调阶段(I/O callbacks)
    6.闲置阶段(idle, prepare)
    7.轮询阶段(按照该顺序反复运行)...
    
    timers 阶段:这个阶段执行timer(setTimeout、setInterval)的回调
    I/O callbacks 阶段:处理一些上一轮循环中的少数未执行的 I/O 回调
    idle, prepare 阶段:仅node内部使用
    poll 阶段:获取新的I/O事件, 适当的条件下node将阻塞在这里
    check 阶段:执行 setImmediate() 的回调
    close callbacks 阶段:执行 socket 的 close 事件回调
    
    注意:
    上面六个阶段都不包括 process.nextTick(),这个函数其实是独立于事件循环之外的,它有一个自己的队列,当每个阶段完成后,
    如果存在 nextTick 队列,就会清空队列中的所有回调函数,并且优先于其他微任务执行
    

    微任务和宏任务

    在js中,任务可以分为同步任务和异步任务,也可以分为微任务和宏任务。同步任务属于宏任务,有了这些划分,就可以保证所有任务都有条不紊的执行下去,总的来说就是给要执行的任务定了执行规则、划分了优先级。
    在总结宏任务与微任务时,我们先要知道我们哪些情况下可能会执行异步操作(未来某个时间执行任务);然后要知道宏任务与微任务是怎么区分的,哪些属于宏任务,哪些属于微任务;最后我们要知道宏任务与微任务是通过什么规则来配合执行的。

    • 可能存在异步执行的情况

      1. 回调函数 callback
      2. Promise/async await
      3. Generator 函数
      4. 事件监听
      5. 发布/订阅
      6. 计时器
      7. requestAnimationFrame
      8. MutationObserver
      9. process.nextTick
      10. I/O
    • 宏任务:

    • 微任务:

    • 注:

      • process.nextTick(它指定的任务总是发生在所有异步任务之前),网上几乎无一例外说这是微任务,可是只要存在这个,process.nextTick就会在所有异步任务执行之前执行
      • 事件监听, 比如addeventlistener。宏任务待验证
      • 发布/订阅 宏任务待验证
      • 有人说同步任务属于宏任务,关于这中说法我觉得不太准确,应该说同步任务的执行优先级是高于异步任务
    • 任务执行过程

      1. 所有任务都在主进程上执行,异步任务会经历2个阶段 Event Table和Event Queue
      2. 同步任务在主进程排队执行,异步任务(包括宏任务和微任务)在事件队列排队等待进入主进程执行
      3. 遇到宏任务推进宏任务队列,遇到微任务推进微任务队列(宏任务队列的项一般对应一个微任务队列,有点像一个大哥带着一群小马仔,这就组成一组异步任务。如果有嵌套那就会有多个大哥小马仔)
      4. 执行宏任务,执行完宏任务,检查有没有当前层的微任务(大哥带着小马仔逐步亮相。。。)
      5. 继续执行下一个宏任务,然后执行对应层次的微任务,直到全部执行完毕(下一个大哥带着他的小马仔亮相。。。)
    • 盗图两张

      • 同步任务与异步任务执行流程
        同步任务与异步任务执行流程
      • 微任务与宏任务执行流程
        微任务与宏任务执行流程

    举个栗子

    下面举个例子加深印象,例子来自网络。当存在多个不同的异步操作时,看宿主环境(node、浏览器等等)是怎么执行的,可以把下面代码或者练习题的代码拷出来,利用浏览器断点看下执行过程。

    //因为涉及到process 所以应该在node环境下执行  
    
    
    console.log('1') //主进程 执行 
    setTimeout(function() {
       console.log('2')   //因为setTimeout是宏任务,所以加入宏任务队列1,['2']
       process.nextTick(function() {
         console.log('3') //因为 process.nextTick是微任务,所以加入微任务队列2,['4','3']
      })
      new Promise(function(resolve) {
          console.log('4') //因为此处代码执行不属于异步,所以直接推入主程序执行,['4']
          resolve()
      }).then(function() {
        console.log('5') // 因为promise then 是微任务,所以推入微任务队列2,['4','3','5']
      })
    },0)
    // process.nextTick总是发生在所有异步任务之前
    process.nextTick(function() {
      console.log('6')  //因为process.nextTick是微任务,所以推入微任务队列1,['6']
      new Promise(function(resolve) {
        console.log('7')//因为此处代码执行不属于异步,所以直接推入主程序执行,['6','7']
        resolve()
      }).then(function() {
        console.log('8')//因为 promise then 是微任务,所以推入微任务队列1,['6','7','8']
      })
      setTimeout(function() {
        console.log('9')//因为setTimeout是宏任务,所以推入宏任务队列2 ,['9']
        process.nextTick(function() {
          console.log('10')//因为process.nextTick是微任务,所以推入微任务队列3,['9','11','12','10']
        })
        new Promise(function(resolve) {
          console.log('11')//因为此处代码执行不属于异步,所以直接推入主程序执行,['9','11']
          resolve()
          console.log('12')////因为此处代码执行不属于异步,所以直接推入主程序执行,['9','11','12']
        }).then(function() {
          console.log('13')//因为 promise then 是微任务,所以推入微任务队列3,['9','11','12','10','12']
        })
      },0)
    })
    
    //打印输出
    // 1
    // 6
    // 7
    // 8
    // 2
    // 4
    // 3
    // 5
    // 9
    // 11
    // 12
    // 10
    // 13
    

    得出结论:微任务优先级大小:process.nextTick > setTimeout

    练习题

    字节笔试题

    async function async1() {        
      console.log('async1 start');
      await async2();
      console.log('async1 end');
    }
    async function async2() {
      console.log('async2'); 
    }
    
    console.log('script start'); 
    setTimeout(function() {
        console.log('setTimeout');
    }, 0);  
    async1();
    new Promise(function(resolve) {
        console.log('promise1');
        resolve();
      }).then(function() {
        console.log('promise2');
    });
    console.log('script end');
    

    参考

  • 相关阅读:
    com.android.ide.common.process.PrecessException:org.gradle.process....finished with non-zero exit value 1
    android 学习笔记(1)
    C#遍历指定文件夹中的所有文件(转)
    让TextView里面的文字逐个显示的动画效果实现(1)
    This Handler class should be static or leaks might occur(null) 解决办法 (转)
    Android开发 旋转屏幕导致Activity重建解决方法(转)
    SQLite 数据库
    【Android】error opening trace file: No such file or directory (2)
    WPF中的RichTextBox
    android中的 Toast 和 AlertDialog
  • 原文地址:https://www.cnblogs.com/xingguozhiming/p/13276725.html
Copyright © 2011-2022 走看看