美文网首页
任务队列,事件循环 宏任务 微任务

任务队列,事件循环 宏任务 微任务

作者: 无人问津的呢喃 | 来源:发表于2021-04-09 15:13 被阅读0次

    javascript执行机制是基于事件循环的并发式的,事件循环负责处理代码,收集和处理事件以及执行队列中的子任务


    可视化描述

    栈(stack)

    js运行时形成一个执行栈,

    function foo(b) {
      let a = 10;
      return a + b + 11;
    }
    
    function bar(x) {
      let y = 3;
      return foo(x * y);
    }
    
    console.log(bar(7)); // 返回 42
    

    当调用 bar 时,第一个帧被创建并压入栈中,帧中包含了 bar 的参数和局部变量。 当 bar 调用 foo 时,第二个帧被创建并被压入栈中,放在第一个帧之上,帧中包含 foo 的参数和局部变量。当 foo 执行完毕然后返回时,第二个帧就被弹出栈(剩下 bar 函数的调用帧 )。当 bar 也执行完毕然后返回时,第一个帧也被弹出,栈就被清空了。

    堆(heap)

    对象被分配在堆中,堆是一个用来表示一大块(通常是非结构化的)内存区域的计算机术语。

    队列(queue)

    一个 JavaScript 运行时包含了一个待处理消息的消息队列。每一个消息都关联着一个用以处理这个消息的回调函数。

    setTimeout(function(){console.log('时间一到,请执行改回调函数')},1000)
    

    事件循环 期间的某个时刻,运行时会从最先进入队列的消息开始处理队列中的消息。被处理的消息会被移出队列,并作为输入参数来调用与之关联的函数。正如前面所提到的,调用一个函数总是会为其创造一个新的栈帧。

    事件循环(event loop)

    queue.waitForMessage() 会同步地等待消息到达(如果当前没有任何消息等待被处理)

    while (queue.waitForMessage()) {
    queue.processNextMessage();
    }

    特点

    1. 单线程

    每一个消息完整地执行后,其它消息才会被执行
    缺点:当一个消息需要太长时间才能处理完毕时,Web应用程序就无法处理与用户的交互,例如点击或滚动
    2.永不阻塞
    JavaScript的事件循环模型与许多其他语言不同的一个非常有趣的特性是,它永不阻塞。 处理 I/O 通常通过事件和回调来执行,所以当一个应用正等待一个 IndexedDB 查询返回或者一个 XHR 请求返回时,它仍然可以处理其它事情,比如用户输入。

    总结一下事件循环的机制:

    (1) 所有任务在执行栈上执行,
    (2) 绑定事件和异步事件(消息)放置于任务队列
    (3) 执行栈执行完毕,一直询问任务队列是否有消息需要执行,如果有就将关联的回调函数放置于执行栈,准备执行

    宏任务(macrotask)和微任务(mircrotask)

    异步任务细分为宏任务和微任务,当一个宏任务执行完,会在渲染前,将执行期间所产生的所有微任务都执行完

    宏任务 -> 微任务 -> GUI渲染 -> 宏任务 -> ...

    宏任务:

    • 主代码块
    • setTimeout
    • setTimeInterval
    • setImmediate()-node
    • requestAnimationFrame -浏览器
    • postMessage
    • I/O
    • UI交互事件
      微任务:
    • process.nextTick ()-Node
    • Promise.then()
    • catch
    • finally
    • Object.observe
    • MutationObserver
    图解宏任务和微任务

    注意点

    1. 浏览器会先执行一个宏任务,紧接着执行当前执行栈产生的微任务,再进行渲染,然后再执行下一个宏任务
    2. 微任务和宏任务不在一个任务队列

    总结

    *执行主线程,遇到异步置入任务队列,并在任务队列根据宏任务和微任务进行区分
    *执行栈完成,查看任务队列,首先查看宏任务队列有没有要执行的任务,没有就过,有就执行

    • 每个宏任务执行完都要查看微任务队列,有没有要执行的任务,没有就过,有就执行,直到微任务队列为空

    测试:

    function test() {
      console.log(1)
      setTimeout(function () {  // timer1
        console.log(2)
      }, 1000)
    }
    
    test();
    
    setTimeout(function () {        // timer2
      console.log(3)
    })
    
    new Promise(function (resolve) {
      console.log(4)
      setTimeout(function () {  // timer3
        console.log(5)
      }, 100)
      resolve()
    }).then(function () {
      setTimeout(function () {  // timer4
        console.log(6)
      }, 0)
      console.log(7)
    })
    
    console.log(8)
    

    结合我们上述的JS运行机制再来看这道题就简单明了的多了
    JS是顺序从上而下执行
    执行到test(),test方法为同步,直接执行,console.log(1)打印1
    test方法中setTimeout为异步宏任务,回调我们把它记做timer1放入宏任务队列
    接着执行,test方法下面有一个setTimeout为异步宏任务,回调我们把它记做timer2放入宏任务队列
    接着执行promise,new Promise是同步任务,直接执行,打印4
    new Promise里面的setTimeout是异步宏任务,回调我们记做timer3放到宏任务队列
    Promise.then是微任务,放到微任务队列
    console.log(8)是同步任务,直接执行,打印8
    主线程任务执行完毕,检查微任务队列中有Promise.then
    开始执行微任务,发现有setTimeout是异步宏任务,记做timer4放到宏任务队列
    微任务队列中的console.log(7)是同步任务,直接执行,打印7
    微任务执行完毕,第一次循环结束
    检查宏任务队列,里面有timer1、timer2、timer3、timer4,四个定时器宏任务,按照定时器延迟时间得到可以执行的顺序,即Event Queue:timer2、timer4、timer3、timer1,依次拿出放入执行栈末尾执行 (插播一条:浏览器 event loop 的 Macrotask queue,就是宏任务队列在每次循环中只会读取一个任务)
    执行timer2,console.log(3)为同步任务,直接执行,打印3
    检查没有微任务,第二次Event Loop结束
    执行timer4,console.log(6)为同步任务,直接执行,打印6
    检查没有微任务,第三次Event Loop结束
    执行timer3,console.log(5)同步任务,直接执行,打印5
    检查没有微任务,第四次Event Loop结束
    执行timer1,console.log(2)同步任务,直接执行,打印2
    检查没有微任务,也没有宏任务,第五次Event Loop结束
    结果:1,4,8,7,3,6,5,2

    console.log('start')
    setTimeout(function(){
      console.log('宏任务1号')
    })
    Promise.resolve().then(function(){
        console.log('微任务0号')
      })
    console.log('执行栈执行中')
    setTimeout(function(){
      console.log('宏任务2号')
      Promise.resolve().then(function(){
        console.log('微任务1号')
      })
    },500)
    
    setTimeout(function(){
      console.log('宏任务3号')
     setTimeout(function(){
      console.log('宏任务4号')
      Promise.resolve().then(function(){
        console.log('微任务2号')
      })
    },500)
     Promise.resolve().then(function(){
        console.log('微任务3号')
      })
    },600)
    console.log('end')
    

    参考

    相关文章

      网友评论

          本文标题:任务队列,事件循环 宏任务 微任务

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