参考:https://www.cnblogs.com/amiezhang/p/11349450.html
https://juejin.im/post/5b73d7a6518825610072b42b

宏任务(同步任务)->微任务->宏任务(异步任务)

1、背景

  • js是单线程的,同一时间只能做一件事,为了协调事件、用户交互、脚本、UI渲染和网络处理等行为,防止主线程的阻塞。

  • js可分为同步任务和异步任务,对于同步的任务,我们是按顺序进行执行,但是对于异步的操作,会有一个优先级的执行顺序,分别为宏任务和微任务。

  • 在js执行时,一个主线程里面都会有一个事件循环(消息循环|运行循环)和事件队列,存放各种要处理的事件信息,通过这个循环不断处理这些事件信息或消息。

2、JavaScript的运行分为

(1) javaScript Engine,Chrome 的引擎就是 V8
(2) Web APIs,DOM 的操作,AJAX,Timeout 等实际上调用的都是这里提供的
(3)Callback Queue,回调的队列,也就是刚刚所有的 Web APIs 里面的回调函数,实际上都是放在这里排队的
(4) EventLoop,事件循环,也就是刚所说的宏任务和微任务的容器

3、事件循环EventLoop

Js的本质是单线

1、一般而言,非阻塞性的任务采用同步的方式,直接在主线程的执行栈中完成。
2、一般来说,阻塞性的任务都会采取异步来执行,异步的工作一般会交给其他线程完成,然后回调函数会放在事件队列中。

时间循环的过程

1、检查还有没有微任务需要处理
2、结束本次宏任务,检查还有没有宏任务需要处理
3、循环这些操作就是event-loop
图片说明

  • 当主线程的任务执行完了(执行栈空了),JS会去询问事件队列

  • 执行一个宏任务(先执行同步代码)-->执行所有微任务-->UI render-->执行下一个宏任务-->执行所有微任务-->UI render-->......

  • 根据HTML Standard,一轮事件循环执行结束之后,下轮事件循环执行之前开始进行UI render。即:macro-task任务执行完毕,接着执行完所有的micro-task任务后,此时本轮循环结束,开始执行UI render。UI render完毕之后接着下一轮循环。但是UI render不一定会执行,因为需要考虑ui渲染消耗的性能已经有没有ui变动

图片说明

4、call Stack

  • 本身就是一个调用栈(就像浏览器中的JavaScript解释器),追踪函数执行流的一种机制,当执行环境调用了多个函数时,通过调用栈,我们可以追踪到哪一个函数在执行,执行的函数体中又调用了哪些函数。
  • 每调用一个函数,解释器就会把该函数添加进调用栈并开始执行。
  • 正在调用栈中执行的函数还调用了其它函数,那么新函数也将会被添加进调用栈,一旦这个函数被调用,便会立即执行。
  • 当前函数执行完毕后,解释器将其清出调用栈,继续执行当前执行环境下的剩余的代码。
  • 当分配的调用栈空间被占满时,会引发“堆栈溢出”。
  • 是存放执行的重要条件,也是因为只有一个调用栈,所以被称为单线程

5、宏任务(macrotasks)

图片说明

  • macrotasks: setTimeout, setInterval, setImmediate, I/O, UI rendering
  • 任务队列中的都是已经完成的异步操作,而不是说注册一个异步任务就会被放在这个任务队列中,就像在银行中排号,如果叫到你的时候你不在,那么你当前的号牌就作废了,柜员会选择直接跳过进行下一个客户的业务处理,等你回来以后还需要重新取号
  • 一个宏任务在执行的过程中,是可以添加一些微任务的,且在当前的微任务没有执行完成时,是不会执行下一个宏任务的。
    图片说明
    setTimeOut是个宏任务,而Promise.then则是微任务,输出是2,4,3,1

所有会进入的异步都是指的事件回调中的那部分代码

  • new Promise在实例化的过程中所执行的代码都是同步进行的,而then中注册的回调才是异步执行的。
  • 在同步代码执行完成之后才回去检查是否有异步任务完成,并执行对应的回调,而微任务又会在宏任务之前执行。
  • +部分表示同步执行的代码
+setTimeout(_ => {
-  console.log(4)
+})

+new Promise(resolve => {
+  resolve()
+  console.log(1)
+}).then(_ => {
-  console.log(3)
+})

+console.log(2)
setTimeout(_ => console.log(4))

new Promise(resolve => {
  resolve()
  console.log(1)
}).then(_ => {
  console.log(3)
  Promise.resolve().then(_ => {
    console.log('before timeout')
  }).then(_ => {
    Promise.resolve().then(_ => {
      console.log('also before timeout')
    })
  })
})

console.log(2)

6、微任务(microtasks)

  • microtasks: process.nextTick, Promises, Object.observe(废弃), MutationObserver
    图片说明