js事件循环运行机制

Posted 索宁风尚

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js事件循环运行机制相关的知识,希望对你有一定的参考价值。

js事件循环之宏任务与微任务

  1. 执行环节node 11,与js文件执行结果可能会有部分差异

  1. javascript中的事件循环

    javascript是单线程执行的,线程中的任务是按照顺序来执行,如果某个任务执行耗时较长,后面的任务就需要排队等待。任务分类:

    • 同步任务
    • 异步任务

    解读:

    • 同步任务与异步任务分别进入不同的执行场所(线程),同步任务进入主线程,异步任务进入Event Table(事件表)并进行函数注册
    • 当Event Table中的异步函数完成后,会在Event Queue(事件队列)中注册回调函数
    • 当主线程中的任务执行完毕后,会去Event Queue中读取对应的函数,推入主线程执行
    • js解析器会不断的存放检查主线程执行栈是否为空,然后重复第三部操作,这个过程也就是我们常说的Event loop(事件循环)

  1. 宏任务与微任务

    宏任务:script(主程序代码),setTimeout,setInterval,setImmediate(node),I/O操作,UI交互,postMessage,MessageChannel...

    微任务:promise(原生promise,有些包装后的promise为宏任务),process.nextTick(),Object.observe,MutaionObserver...

    区别:宏任务与微任务都是异步任务,都在同一个队列(进程)中,主要区别在于他们的执行顺序,Event loop的走向和取值

    解读:

    • 在异步环境中,宏任务在微任务之前执行。遇到宏任务,会先将宏任务放入Event Queue中,然后再执行微任务,将微任务放入Event Queue中。
    • 宏任务与微任务对应的Event Queue不是同一个Event Queue,在往外拿(把回调函数推入主线程执行)的过程中,先拿微任务Event Queue中的回调函数,再拿宏任务Event Queue中的回调函数
    • 执行过程:异步线程中 => 主线程中任务执行为空(执行栈为空) => 宏任务进Event Queue => 微任务进Event Queue => 微任务回调函数出Event Queue进主线程 => 宏任务回调函数出Event Queue进主线程 => 主线程中任务执行为空(执行栈为空) => ... => Event loop事件循环重复执行过程(判断主线程任务执行结束,把异步任务推到主线程的循环执行过程) => ... => 所有任务执行结束

  1. 实例
  // 实例1
  console.log(1)

  new Promise((resolve, reject) => {
    console.log(2)
    resolve()
    console.log(3)
    reject()
    console.log(4)
  })
  .then(() => {
    console.log(5)
  })

  console.log(6)
  // 1, 2, 3, 4, 6, 5

原因:

  • promise构造函数里面是同步执行的
  • then方法指向的回调将会在当前线程同步任务执行完之后执行
  • 结果就是1,2,3,4,6,5

  console.log(1)

  setTimeout(() => console.log(2), 0) // fn1

  setTimeout(() => console.log(3), 1000)  // fn2

  new Promise((resolve) => {  // fn3
    console.log(4)
    resolve()
  })
  .then(() => console.log(5)) // fn4

  setTimeout(() => console.log(6), 0) // fn5

  console.log(7)


  // 1, 4, 7, 5, 2, 6, 3

原因:

  • promise构造函数里面是同步执行的
  • then方法指向的回调将会在当前线程同步任务执行完之后执行
  • consoel.log()方法,promise构造函数会放到同步线程中,输出1 => fn3 => 4 => 7,,promise().then()方法,setTimeout函数会放在异步线程中去,setTimeout为宏任务,promise().then()为微任务,因此先执行fn4 => 5 => fn1 => fn2 => fn5,其中fn2延迟比fn5时间长,因此fn5会先返回,输出为fn4 => 5 => fn1 => 2 => fn5 => 6 => fn2 => 3

  console.log(1)

  setTimeout(() => console.log(2), 0) // fn1

  console.log(3)

  setTimeout(() => console.log(4), 1000)  // fn2

  Promise.resolve()
    .then(() => { // fn3
      setTimeout(() => console.log(5), 0) // fn4
      setTimeout(() => console.log(6), 1000)  // fn5
      console.log(7)
      Promise.resolve()
        .then(() => console.log(8)) // fn6
    })
    .then(() => console.log(9)) // fn7

  new Promise((resolve) => {  // fn8
    console.log(10)
    resolve()
  }).then(() => { // fn9
    console.log(11)
  })

  setTimeout(() => console.log(12), 0)  // fn10

  console.log(13)

  setTimeout(() => console.log(14), 1000) // fn11
  
  // 结果
  // 1, 3, 10, 13, 7, 11, 8, 9, 2, 12, 5, 4, 14, 6

原因:

  • promise构造函数里面是同步执行的
  • then方法指向的回调将会在当前线程同步任务执行完之后执行
  • 同上原因,1,3,fn8,13同步执行,输出:1 => 3 => fn8 => 10 => 13,主线程执行结束。微任务开始执行:然后在执行promise().then()方法,fn3在fn9前面定义,输出:fn3 => 7 => fn9 => 11,主线程执行结束。执行fn3中定义的promise().then()方法,再执行fn3本身的then()方法,输出:fn6 => 8 => fn7 => 9,主线程执行结束。宏任务开始执行:fn1 => fn2 => fn10 => fn11 => fn4 => fn5,然后通过延迟时间,输出:fn1 => 2 => fn10 => 12 => fn4 => 5 => fn2 => 4 => fn11 => 13 => fn5 => 6
  • 输出:1 => 3 => fn8 => 10 => 13 => fn3 => 7 => fn9 => 11 => fn6 => 8 => fn7 => 9 => fn1 => 2 => fn10 => 12 => fn4 => 5 => fn2 => 4 => fn11 => 14 => fn5 => 6
  • 最终结果:1, 3, 10, 13, 7, 11, 8, 9, 2, 12, 5, 4, 14, 6





参考文章

[1]JS事件循环机制(event loop)之宏任务/微任务
[2]阮一峰老师ES6入门

以上是关于js事件循环运行机制的主要内容,如果未能解决你的问题,请参考以下文章

js事件循环运行机制

js运行机制 (包括宏任务微任务,同步异步,事件循环机制Event Loop)面试常问

js事件循环机制辨析

js事件循环机制

JS事件循环机制(宏任务,微任务)

JS事件循环机制(宏任务,微任务)