微任务和宏任务

281 阅读2分钟

为什么任务要分为同步任务和异步任务

试想一下,如果js的任务都是同步(书写顺序和执行顺序一致)的,那么遇到定时器、网络请求等这类型需要延时执行的任务会发生什么?

页面可能会瘫痪,需要暂停下来等待这些需要很长时间才能执行完毕的代码

所以,又引入了异步任务。

  • 同步任务:同步任务不需要进行等待,必须立即看到执行结果,比如console
  • 异步任务:异步任务需要等待一定的时候才能看到结果,比如setTimeout、网络请求

宏任务和微任务

异步任务,又可以细分为宏任务和微任务。下面列举常用的宏任务和微任务。

任务(代码)宏/微 任务环境
script宏任务浏览器
事件宏任务浏览器
网络请求(Ajax)宏任务浏览器
setTimeout() 定时器宏任务浏览器 / Node
fs.readFile() 读取文件宏任务Node
Promise.then()微任务浏览器 / Node

示例

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

输出结果:1,5,2,4,3

分析:

  • 先执行同步代码
  • 遇到宏任务,放入队列
  • 遇到微任务,放入微任务队列
  • 执行栈为空
    • 将微任务入栈执行
  • 所有的微任务完成之后,取出宏任务队列来执行

案例1

console.log(1)
setTimeout(function() {
  console.log(2)
  new Promise(function(resolve) {
    console.log(3)
    resolve()
  }).then(function() {
    console.log(4)
  })
})

new Promise(function(resolve) {
  console.log(5)
  resolve()
}).then(function() {
  console.log(6)
})
setTimeout(function() {
  console.log(7)
  new Promise(function(resolve) {
    console.log(8)
    resolve()
  }).then(function() {
    console.log(9)
  })
})
console.log(10)

输出结果:1,5,10,6,2,3,4,7,8,9

思路分析

image.png

案例2

<script>
    console.log(1);
    async function fnOne() {
      console.log(2);
      await fnTwo(); // 右结合先执行右侧的代码, 然后等待
      console.log(3);
    }
    async function fnTwo() {
      console.log(4);
    }
    fnOne();
    setTimeout(() => {
      console.log(5);
    }, 2000);
    let p = new Promise((resolve, reject) => { // new Promise()里的函数体会马上执行所有代码
      console.log(6);
      resolve();
      console.log(7);
    })
    setTimeout(() => {
      console.log(8)
    }, 0)
    p.then(() => {
      console.log(9);
    })
    console.log(10);
  </script>
1,2,4,6710,3,9,8,5
 <script>
    console.log(11);
    setTimeout(() => {
      console.log(12);
      let p = new Promise((resolve) => {
        resolve(13);
      })
      p.then(res => {
        console.log(res);
      })
      console.log(15);
    }, 0)
    console.log(14);
  </script>
1114,12,15,13

案例3

使用return,翻转执行顺序

Promise.resolve()
    .then(() => {
      console.log(0)
      // 跳出函数,不将.then加入下一次队列
      return Promise.resolve(4)
    }).then(res => {
      console.log(res)
    })
    .then(() => {
      console.log(7)
    }).then(() => {
      console.log(8)
    })

    Promise.resolve()
    .then(() => {
      console.log(1)
    }).then(() => {
      console.log(2)
    }).then(() => {
      console.log(3)
    }).then(() => {
      console.log(5)
    }).then(() => {
      console.log(6)
    })

    Promise.resolve()
    .then(() => {
      console.log(555);
    }).then(() => {
      console.log(666);
    }).then(() => {
      console.log(777);
    }).then(() => {
      console.log(888);
    }).then(() => {
      console.log(999);
    })

结果:0,1,555,2,666,3,777,4,5,888,7,6,999,8

解析: 每一个.then都是一个微任务,一个.then执行完后,会将下一次.then加入到队列中。

image.png