宏任务微任务的定义

204 阅读4分钟

宏任务微任务的定义

宏任务

宏任务通常指那些执行时间较长,或者可以独立于其他任务的任务。常见的宏任务包括:

  • setTimeout
  • setInterval
  • setImmediate (Node.js 环境)
  • I/O

1.setTimeout

  • 在指定的延迟时间后执行一次回调函数
  • 适用于 单次延迟任务(如延迟加载、防抖/节流)。
setTimeout(() => {
  console.log("2秒后执行");
}, 2000);

2.setInterval

  • 每隔指定的时间重复执行回调函数
  • 适用于 周期性任务(如轮询、动画)。
let count = 0;
const timer = setInterval(() => {
  console.log(`第 ${++count} 次执行`);
  if (count >= 5) clearInterval(timer); // 停止
}, 1000);

3.setImmediate(Node.js特有)

  • 在当前事件循环的检查阶段(check)执行回调,执行一次
  • 适用于 希望代码尽快执行 的场景。

4.I/O(输入输出)

  • 异步非阻塞特性:I/O 操作(如文件读写、网络请求)需要等待系统底层完成,完成后回调被推入宏任务队列。
  • 与同步代码分离:I/O 回调不会立即执行,而是等待事件循环轮到I/O宏任务队列时处理。

Node.js事件循环阶段:

┌───────────────────────┐
│        Timers         │ ◀─ `setTimeout`/`setInterval`
└──────────┬────────────┘
           │
┌──────────▼────────────┐
│     I/O Callbacks     │ ◀─ **已完成 I/O 的回调**(如 `fs.readFile`、网络请求)
└──────────┬────────────┘
           │
┌──────────▼────────────┐
│       Idle/Prepare    │ ◀─ 内部使用
└──────────┬────────────┘
           │
┌──────────▼────────────┐
│        Poll           │ ◀─ 检索新的 I/O 事件(如新连接、文件读取)
└──────────┬────────────┘
           │
┌──────────▼────────────┐
│        Check          │ ◀─ `setImmediate` 回调
└──────────┬────────────┘
           │
┌──────────▼────────────┐
│    Close Callbacks    │ ◀─ 关闭事件(如 `socket.on('close')`)
└───────────────────────┘

例子:

const fs = require("fs");

console.log("同步代码 1");

fs.readFile("file.txt", (err, data) => {
  console.log("I/O 回调执行"); // 宏任务
});

setTimeout(() => console.log("setTimeout"), 0);
setImmediate(() => console.log("setImmediate"));

console.log("同步代码 2");

/**
 * 输出顺序:
 * 同步代码 1
 * 同步代码 2
 * setTimeout       (Timers Phase)
 * I/O 回调执行      (I/O Callbacks Phase)
 * setImmediate     (Check Phase)
 */

微任务

微任务通常指那些执行时间短,更细粒度的任务,比宏任务优先级高。常见的微任务包括:

  • Promise.then/catch/finally
  • MutationObserver
  • queueMicrotask (浏览器、Node.js环境)
  • process.nextTick (Node.js 环境)

Promise.then/catch/finally

  1. 在 Promise 中,只有 thencatchfinally 的回调会被放入微任务队列,而其他 Promise 方法(如 Promise.resolve()Promise.reject()Promise.all()Promise.race() 等)本身不会直接产生微任务,它们只是用于构造或处理 Promise 对象。
  2. Promise 构造函数里的 resolve / reject / all / race / allSettled / any 是同步的
new Promise((resolve) => {
  console.log("同步执行"); // 同步代码
  resolve("微任务触发");
}).then((val) => console.log(val)); // 微任务
  • new Promise(fn) 的 fn 是 同步执行 的。
  • resolve() 或 reject()它们只是 同步 返回一个 Promise 对象,不会直接产生微任务,只有 resolve() 或 reject() 后,then/catch/finally 才会安排微任务。

MutationObserver

MutationObserver是浏览器提供的一个 异步监听 DOM 变化 的 API。

const observer = new MutationObserver((mutations) => {
  console.log("DOM 发生变化!", mutations);
});

observer.observe(document.body, {
  childList: true,    // 监听子节点变化
  attributes: true,   // 监听属性变化
  subtree: true       // 监听所有后代节点
});

// 触发 DOM 变化
document.body.appendChild(document.createElement("div"));
  • MutationObserver 的回调是微任务,不会立即执行,而是进入微任务队列。
  • 即使多次修改 DOM,回调只会执行一次(合并变化)。
  • MutationObserver 和 Promise 都是微任务,按注册顺序执行。
// 短时间内多次修改 DOM
document.body.appendChild(document.createElement("div"));
document.body.appendChild(document.createElement("p"));
document.body.setAttribute("data-test", "123");

// MutationObserver 回调只会执行一次,包含所有变更记录

queueMicrotask

queueMicrotask 是一个用于将回调函数加入 微任务队列(Microtask Queue)  的全局方法,在 浏览器 和 Node.js 环境中均可使用。

作用
  • 它接收一个回调函数,并将其添加到 当前调用栈执行完毕后的微任务队列 中。
  • 类似于 Promise.resolve().then(callback),但更直接,不需要创建 Promise 实例。
  • 执行时机和 Promise 微任务相同,但比 process.nextTick 优先级低(在 Node.js 中)。
console.log("同步任务 1");

queueMicrotask(() => {
  console.log("微任务 1");
});

process.nextTick(() => {
  console.log("nextTick 微任务");
});

console.log("同步任务 2");

// 输出顺序:
// 同步任务 1
// 同步任务 2
// nextTick 微任务  (nextTick 优先级更高)
// 微任务 1

process.nextTick

process.nextTick 在 Node.js 中被视为一种微任务(Microtask) ,但它不属于标准的 Promise 微任务队列,而是属于 Next Tick Queue,具有更高的优先级。

一段容易错的题

fs.readFile("file.txt", () => {
  console.log("I/O 回调(宏任务)");

  Promise.resolve().then(() => {
    console.log("微任务"); // 在 I/O 回调后立即执行
  });
});
//I/O 回调(宏任务)
//微任务

我在第一反应是先输出微任务O.O,理由是先执行微任务在执行宏任务,大错特错!

注意:

微任务不会抢占当前正在执行的宏任务,而是在 当前宏任务完成后 执行。

微任务是在当前宏任务执行完后、下一个宏任务开始前执行的

所以不管是什么宏任务里面套了微任务,都要注意这一点。