JS | 宏任务、微任务

在熟悉宏任务,微任务之前,需要先回忆一个名词:事件循环👊

什么是事件循环?

只要一问这些概念性的东西,总是一脸懵的尴尬,然后开始心底没底的表述就是常说的宏任务、微任务,只想着把注意力引到自己熟悉的地方。

那怎样理解,怎样回答才是最优解呢?

最简洁的描述:一种机制,负责管理任务的调度和执行。

为何需要这种机制?

老生常谈的单线程语言一次只能执行一个任务,

但在实际应用中,我们需要处理很多异步任务,例如网络请求、定时器、事件处理等。

这就是事件循环的作用,它确保异步任务按照正确的顺序执行。

1、什么是宏任务,微任务?

  • 宏任务:一个较大的任务单元。
  • 微任务:一个较小的任务单元。

2、宏任务、微任务常见有哪些?

宏任务:

  • script(JS 整体代码)、setTimeout、setInterval、I/O、UI 交互

微任务:

  • Promise 回调:当一个 Promise 的状态变为 resolved 或 rejected 时,与之关联的回调函数会被放入微任务队列。

3、为什么会出现微任务?

微任务的出现其实就是语言设计中的一种实时性和效率的权衡体现。当宏任务执行时间太久,就会影响到后续任务的执行,而此时因为某些需求,突然有高优先级的任务需要尽快执行。(从此处可知在事件循环的设计中,微任务被赋予了更高的优先级,优先执行)

4、那浏览器环境与node环境执行宏任务、微任务是否存在区别?

  • node 中事件循环的实现依赖 libuv 引擎,它有一套自己的循环机制
  • node10之后的版本,几乎与浏览器保持一致

5、事件循环的基本流程如下

  • 执行当前宏任务(从宏任务队列中取出一个任务执行)。
  • 执行所有微任务(从微任务队列中取出并执行所有微任务)。

渲染页面更新(如果需要)。

  • 进入下一个事件循环。

6、基于案例执行

  1. 所有代码(JS 整体代码)作为宏任务进入主线程执行栈执行
  2. 执行过程中,同步代码立即执行。遇到微任务,将其添加至微任务队列中。遇到宏任务,将其添加到宏任务队列中
  3. 当前宏任务出队完成,读取微任务队列,直到执行完所有微任务( 即微任务队列为空 )。本轮宏任务执行完成
  4. 队列若存在宏任务,出队回到步骤2,继续循环执行,直到宏任务队列清空

案例:

function taskOne() {
    console.log('task one ...')
    setTimeout(() => {
        Promise.resolve().then(() => {
            console.log('task one micro in macro ...')
        })
        setTimeout(() => {
            console.log('task one macro ...')
        }, 0)
    }, 0)
    taskTwo()
}
function taskTwo() {
    console.log('task two ...')
    Promise.resolve().then(() => {
        setTimeout(() => {
            console.log('task two macro in micro...')
        }, 0)
    })
    setTimeout(() => {
        console.log('task two macro ...')
    }, 0)
}
setTimeout(() => {
    console.log('running macro ...')
}, 0)
taskOne()
Promise.resolve().then(() => {
    console.log('running micro ...')
})

补充说明

面试遇到类似输出题目,别紧张,基于队列的先入先出规则,列出列队中的任务,
不要盲目自信依赖大脑的记忆力,不然会造成其实你会,只是记忆断片了,忘记队列里哪个是先进去的导致的尴尬场景~

1、宏任务队列

2、微任务队列

3、输出结果