关于JS事件循环和任务队列的一点思考

47 阅读2分钟

一:js的两个特点

  • 单线程异步无阻塞
  • 基于事件驱动

二:主线程执行中遇到异步任务,会放到宏任务或微任务队列,继续执行主线程

1,宏任务

  • script整体代码、
  • setTimeout、
  • setInterval、
  • setImmediate、
  • [Ajax]、DOM事件

2,微任务

  • process.nextTick、
  • MutationObserver、
  • Promise.then
  • catch finally

3,微任务先执行,而且是一次性要把微任务队列里的任务都执行完再去执行宏任务

setTimeout(()=>{
  console.log('s1')
  Promise.resolve().then(()=>{
    console.log('p1')
  })
  Promise.resolve().then(()=>{
    console.log('p2')
  })
})

setTimeout(()=>{
  console.log('s2')
  Promise.resolve().then(()=>{
    console.log('p3')
  })
  Promise.resolve().then(()=>{
    console.log('p4')
  })
})
s1
p1
p2
s2
p3
p4

代码执行顺序

  1. 此时宏任务队列和微任务队列都为空,开始执行代码
  2. 遇到定时器,是宏任务,放到宏任务队列,继续往下执行
  3. 又遇到一个定时器,是宏任务,放到宏任务队列,继续往下执行
  4. 主线程同步代码执行完毕,接着来看任务队列,先看微任务队列,为空,再看宏任务队列,有两个任务
  5. 先执行第一个宏任务
    • 1)打印 s1
    • 2)遇到一个Promise微任务,添加到微任务队列
    • 3)又遇到一个Promise微任务,添加到微任务队列
  6. 每执行完一个宏任务,都会去看微任务队列有没有,有就全部执行完,此时有两个微任务,所以打印出 p1 p2
  7. 微任务执行完毕,再去执行下一个宏任务
    • 1)打印 s2
    • 2)遇到一个Promise微任务,添加到微任务队列
    • 3)又遇到一个Promise微任务,添加到微任务队列
  8. 这个宏任务执行完,再去执行微任务 打印出 p3 p4

三:现在在微任务里再加入宏任务

setTimeout(()=>{
  console.log('s1')
  Promise.resolve().then(()=>{
    console.log('p1')
    setTimeout(() => {
      console.log('h1')
    });
  })
  Promise.resolve().then(()=>{
    console.log('p2')
    setTimeout(() => {
      console.log('h2')
    });
  })
})

setTimeout(()=>{
  console.log('s2')
  Promise.resolve().then(()=>{
    console.log('p3')
    setTimeout(() => {
      console.log('h3')
    });
  })
  Promise.resolve().then(()=>{
    console.log('p4')
    setTimeout(() => {
      console.log('h4')
    });
  })
})

按照上面的逻辑顺序结果应该是

s1
p1
p2
s2
p3
p4
h1
h2
h3
h4