0Pricing
JavaScript Academy · 课时

微任务与 Promise

了解 Promise 回调为何会优先运行。

微任务与 Promise 是 CoddyKit 上的免费 JavaScript Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 JavaScript Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 JavaScript Academy 课程共包含 4 节课。

什么是微任务?

微任务是一种高优先级的延迟执行任务。承诺对象的回调函数(.then、.catch、.finally)以及 queueMicrotask 都会安排微任务。

关键规则是:每次同步执行结束后,必须先清空所有微任务,然后才能执行任何宏任务。

承诺对象的回调会延迟执行

即使承诺对象已经解决,它的 .then 回调函数也会作为微任务被安排,而不会同步运行。

console.log('start');
Promise.resolve().then(() => console.log('promise then'));
console.log('end');

微任务优先于宏任务

这是最重要的执行顺序规则。承诺对象的回调函数会在 setTimeout 回调函数之前运行,即使定时器是先安排的。

setTimeout(() => console.log('timeout (macro)'), 0);
Promise.resolve().then(() => console.log('promise (micro)'));
console.log('sync');

微任务队列会被完全清空

事件循环会先完全清空微任务队列,然后才获取下一个宏任务。多个承诺对象的回调函数会在这次清空过程中连续运行。

setTimeout(() => console.log('macro'), 0);
Promise.resolve().then(() => console.log('micro 1'));
Promise.resolve().then(() => console.log('micro 2'));
console.log('sync');

链式承诺对象

链中的每个 .then 都会在前一个处理完成后安排一个新的微任务。它们仍然会在任何等待中的定时器之前全部运行。

setTimeout(() => console.log('timeout'), 0);
Promise.resolve()
  .then(() => console.log('then 1'))
  .then(() => console.log('then 2'))
  .then(() => console.log('then 3'));
console.log('sync');

清空过程中安排的微任务

如果一个微任务安排了另一个微任务,新的微任务也会在同一次清空过程中运行,并且先于任何宏任务。队列会持续清空,直到真正为空。

Promise.resolve().then(() => {
  console.log('micro A');
  Promise.resolve().then(() => console.log('micro B (added later)'));
});
setTimeout(() => console.log('macro'), 0);
console.log('sync');

解决后的值会继续传递

.then 处理函数返回的值会成为链中下一个处理函数的输入,整个过程都通过微任务完成。

Promise.resolve(2)
  .then(n => n * 3)
  .then(n => n + 1)
  .then(n => console.log('result', n));
console.log('sync first');

异步/等待使用微任务

await 之后的代码会作为微任务恢复执行。因此,等待操作的后续代码会在较早安排的定时器之前运行。

async function run() {
  console.log('a');
  await null;
  console.log('c (after await)');
}
setTimeout(() => console.log('d (timeout)'), 0);
run();
console.log('b');

综合执行顺序示例

把所有规则结合起来。请预测执行顺序:先执行同步代码,然后执行所有微任务,最后执行宏任务。

console.log('1 sync');
setTimeout(() => console.log('5 timeout'), 0);
Promise.resolve().then(() => console.log('3 promise'));
Promise.resolve().then(() => console.log('4 promise'));
console.log('2 sync');

微任务饥饿为何有风险

由于微任务总是在宏任务之前运行,一个不断安排更多微任务的微任务可能会阻塞定时器和渲染。请确保微任务链最终会结束。

let n = 0;
function loop() {
  if (n++ < 3) {
    console.log('micro ' + n);
    Promise.resolve().then(loop);
  }
}
setTimeout(() => console.log('macro after micros'), 0);
loop();

这种执行顺序为何重要

了解微任务先于宏任务运行,可以解释为什么承诺对象的结果会出现在定时器回调函数之前,以及执行顺序错误为何会发生。规则很简单:同步执行,然后清空所有微任务,再执行一个宏任务,如此重复。

console.log('A');
Promise.resolve().then(() => console.log('B'));
setTimeout(() => console.log('D'), 0);
console.log('C');

快速检查

一个承诺对象的 .then 和一个 setTimeout(fn, 0) 都在等待执行。哪个会先运行?

回顾:微任务与承诺对象

您已经学会了优先级规则:

  • 微任务来自承诺对象和 queueMicrotask。
  • 必须先清空整个微任务队列,然后才能执行任何宏任务。
  • async/await 的后续执行属于微任务。
  • 无休止的微任务链可能会阻塞定时器和渲染。

常见问题解答

「微任务与 Promise」课时是免费的吗?

是的 — 「微任务与 Promise」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 JavaScript Academy 课程的其余内容,请升级到 CoddyKit PRO。 JavaScript Academy 课程共包含 4 节课。

「微任务与 Promise」这节课中我会学到什么?

了解 Promise 回调为何会优先运行。 你通过在浏览器中直接运行的动手代码来练习 JavaScript Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 JavaScript Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 JavaScript Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「微任务与 Promise」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 JavaScript Academy 课中编写并运行代码吗?

能。每节 JavaScript Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 调用栈
  2. 宏任务与任务队列
  3. 微任务与 Promise
  4. queueMicrotask 与执行顺序
← 返回 JavaScript Academy