前言:JavaScript是一个单线程的脚本语言。就是说在一行代码执行的过程中,必然不会存在同时执行的另一行代码。同一时间只能做一件事,为了协调事件、用户交互、脚本、UI渲染和网络处理行为,防止主线程的不阻塞,Event Loop的方案应用而生。

一、宏任务和微任务的区别

       (1)宏任务

                (macro)task,可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事事件队列中获取一个事件回调并放到执行栈中执行)。

               游览器为了能够使得JS内部(macro)task与DOM任务能够有序的执行,会在一个(macro)task执行结束后,在下一个(macro)task执行开始前,对页面进行重新渲染,流程如下:

(macro)task ->渲染->(macro)task->...

宏任务包含:

script(整体代码)

setTimeout

setInterval

I/O

UI交互事件

postMessage

MessageChannel

setImmediate(Node.js环境) 

       (2)微任务

       microtask,可以理解是在当前task执行结束后立即执行的任务。也就是说,在当前task任务后,下一个task之前,在渲染之前。

       所以,它的响应速度相比setTimeout(setTimeout是task)会更快,因为无需等渲染。也就是说,在某一个macrotask执行完后,就会将在它执行期间产生的所有microtask都执行完毕(在渲染前)。

       微任务包含:

Promise.then

Object.observe

MutaionObserver

process.nextTick(Node.js环境) 

         (3)运行机制

         在事件循环中,没进行一次循环操作称为tick,每一次tick的任务处理模型是比较复杂的,但关键步骤如下:

  • 执行一个宏任务(栈中没有就从事件队列中获取);
  • 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中;
  • 宏任务执行完成后,立即执行当前微任务队列中的所有微任务(依次执行);
  • 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染;
  • 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)。

        如图:

wps js宏使用jquery_宏任务与微任务

         (4)宏任务与微任务的区别

          任务队列中的都是已经完成的异步操作,而不是说注册一个异步任务就会被放在这个任务队列中。而且一个宏任务在执行的过程中,是可以 添加一些微任务的。在当前的微任务没有执行完成时,是不会执行下一个宏任务的。

二、经常在面试题、各种博客中出现的代码片段

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

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

console.log(2)

setTimeout就是作为宏任务来存在的,而Promise.then则是具有代表性的微任务,上述代码的执行顺序就是按照序号来输出的。

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

也就是说,new Promise在实例化的过程中所执行的代码都是同步进行的,而then中注册的回调才是异步执行的。

在同步代码执行完成后才回去检查是否有异步任务完成,并执行对应的回调,而微任务又会在宏任务之前执行。

所以,就得到了上述的输出结论1、2、3、4。

+部分表示同步执行的代码

+setTimeout(_ => {
-  console.log(4)
+})

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

+console.log(2)

  本来setTimeout已经先设置了定时器(相当于取号),然后在当前进程中又添加了一些Promise的处理(临时添加业务)。

 参考博客:微任务、宏任务与Event-Loop 

                   js中的宏任务与微任务