什么是宏任务和微任务

213 阅读3分钟

事件循环 eventloop - 面试

js运行在宿主环境中。 javascript的宿主环境有:浏览器,nodejs

提到事件循环时候,是指宿主环境的事件循环,而不是js中的事件循环。

为什么呢?

  1. 我们常见的事件,点击事件,鼠标事件等等是不是在浏览器中提到的?

  2. 浏览器本身是一个复杂的系统,他要做得事情非常多,例如执行js代码,请求图片资源,解析css,渲染页面,响应鼠标的点击事件等等,在实现层面,浏览器内部会用不同的功能模块去完成不同的事情,这些不同的模块就体现为 进程

进一步把进程进行划分:

  1. 主进程。用来协调控制其他子进程。
  2. GPU进程。用于3D绘制等。
  3. 渲染进程。就是我们说的浏览器内核,负责具体页面的渲染,脚本执行,事件处理等。浏览器的每个tab页背后就有一个渲染进程。

宏任务和微任务

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

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

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

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

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

Snipaste_2022-05-16_21-39-23.png

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')

分析:

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

yuque_diagram.jpg

面试题:

console.log(1)

setTimeout(function() {
  console.log(2)
}, 0)

const p = new Promise((resolve, reject) => {
  resolve(1000)
})
p.then(data => {
  console.log(data)
})

console.log(3)

分析:

浏览器首先会执行同步代码 所以,console 1和3会优先执行 当遇到setTimeout这个宏任务时,会先放在宏任务队列, 然后执行到Promise微任务时会将他放在微任务队列,浏览器会再执行Promise 输出1000

结果打印是1,3,1000,2

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

  console.log(1)

  setTimeout(function() {
    console.log(2)
  }, 0)

  const p = new Promise((resolve, reject) => {
    console.log(3)
    resolve(1000) // 标记为成功
    console.log(4)
  })

  p.then(data => {
    console.log(data)
  })

  console.log(5)

打印结果: 1,3,4,5,1000,2

  new Promise((resolve, reject) => {
    resolve(1)

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

  }).then(data => {
    console.log(data)
  })

  console.log(3)

打印结果: 3,2,1

setTimeout(() => {
  console.log(1)
}, 0)
new Promise((resolve, reject) => {
  console.log(2)
  resolve('p1')

  new Promise((resolve, reject) => {
    console.log(3)
    setTimeout(() => {
      resolve('setTimeout2')
      console.log(4)
    }, 0)
    resolve('p2')
  }).then(data => {
    console.log(data)
  })

  setTimeout(() => {
    resolve('setTimeout1')
    console.log(5)
  }, 0)
}).then(data => {
  console.log(data)
})
console.log(6)

结果: 2,3,6,p2, p1, 1, 4, 5

<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,6,7,10,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>
  11,14,12,15,13