深入解析 JavaScript 中 Promise 的执行顺序

作者:闭包旅人

时间:2026-10-31

浏览:404

本文详细解析 JavaScript 中 Promise 的执行机制,重点阐述微任务与宏任务的优先级差异。通过具体代码示例,分析同步任务、Promise 回调及 setTimeout 的执行顺序,并探讨链式调用中 return 语句对微任务队列的影响,帮助开发者准确掌握异步编程的执行流程。

文章目录

概述

在分析 Promise 的执行流程时,有两个核心概念需要明确:

  • 微任务与宏任务的优先级:

    • 微任务:Promise.then()、catch、finally 属于微任务范畴。
    • 宏任务:setTimeout、setInterval 属于宏任务范畴。
    • 微任务的优先级高于宏任务:在单次事件循环(Event Loop)中,引擎会优先清空所有微任务队列,之后才会执行下一个宏任务。
  • Promise 是基于微任务实现的:

    • 当一个 Promise 的状态转变为 resolved 或 rejected 时,其关联的 .then() 回调函数会被添加到微任务队列中,等待当前同步任务(含已入队的微任务)执行完毕后触发。

示例代码分析

代码分析

以下代码示例展示了 Promise 和 setTimeout 的执行顺序:

console.log("script start");

setTimeout(() => {
  console.log("setTimeout 1");
}, 0);

Promise.resolve()
  .then(() => {
    console.log("promise 1");
    return Promise.resolve().then(() => {
      console.log("promise 2");
    });
  })
  .then(() => {
    console.log("promise 3");
  });

setTimeout(() => {
  console.log("setTimeout 2");
}, 0);

console.log("script end");

执行过程解析

  • 同步任务:立即执行

    • console.log("script start") 输出 "script start".
    • setTimeout 的两个回调函数被放入宏任务队列,等待事件循环调度。
    • Promise.resolve() 被调用,then() 的回调被放入微任务队列。

    输出结果:

  • script start script end

  • 主线程执行完同步任务后,开始执行微任务队列

    • 微任务队列的顺序如下:
      • 第一个 .then() 输出 "promise 1" 并返回一个新的 Promise。
      • 新的 Promise.then() 输出 "promise 2"。
      • 第二个 .then() 输出 "promise 3"。

    输出结果:

    promise 1
    promise 2
    promise 3
    
  • 清空微任务队列后,开始执行宏任务队列

    • 宏任务队列的两个 setTimeout 回调依次执行,输出 "setTimeout 1" 和 "setTimeout 2"。

    输出结果:

    setTimeout 1
    setTimeout 2
    

最终输出

综合以上,代码的输出顺序为:

script start
script end
promise 1
promise 2
promise 3
setTimeout 1
setTimeout 2

总结

  • 同步任务优先执行,输出 script start 和 script end。
  • 微任务队列优先于宏任务队列。
  • Promise.then() 的回调会依次进入微任务队列。
  • setTimeout 的回调进入宏任务队列,最后执行。

(拓展)问题补充

如果第一个then不是返回return promise,而是直接执行一个Promise.resolve().then(() => { console.log("promise 2"); });结果是不是会变成 promise 1 → promise 3 → promise 2。结果是的。

为什么会这样?

当 Promise.resolve().then() 不通过 return 将内部的 Promise 链接到外部 then 时,promise 2 的执行不再是当前链的一部分,它会被单独添加到微任务队列的末尾,导致执行顺序的变化。

示例代码

以下是修改后的代码:

console.log("script start");

Promise.resolve()
  .then(() => {
    console.log("promise 1");
    Promise.resolve().then(() => {
      console.log("promise 2");
    });
  })
  .then(() => {
    console.log("promise 3");
  });

console.log("script end");

执行过程解析

  • 同步任务

    • 输出 "script start"。
    • 主线程继续,将第一个 Promise.then() 的回调加入微任务队列。
    • 输出 "script end"。

    当前输出:

    script start
    script end
    
  • 微任务队列开始执行

    • 执行第一个 .then(),输出 "promise 1"。在此回调中,一个新的微任务(promise 2 的回调)被加入微任务队列末尾。
    • 执行第二个 .then() 的回调,输出 "promise 3"。

    当前输出:

    promise 1
    promise 3
    
  • 微任务队列剩余任务

    • 微任务队列中剩余的任务是 promise 2 的回调,输出 "promise 2"。

    最终输出:

    promise 2
    

总输出结果

综合以上,完整的输出顺序是:

script start
script end
promise 1
promise 3
promise 2

关键点解析

  • 链式调用和微任务队列当你不通过 return 将一个新的 Promise 链接到当前 then,它的回调会独立加入微任务队列的末尾,而不是成为当前链的一部分。

  • 对比:返回 Promise如果 return Promise.resolve().then(...),那么 promise 2 的执行会成为当前链的一部分,顺序为 promise 1 → promise 2 → promise 3。

改变的核心代码

  • 独立的微任务:

    Promise.resolve().then(() => {
      console.log("promise 2");
    });
    
  • 作为链的一部分:

    return Promise.resolve().then(() => {
      console.log("promise 2");
    });
    

两种写法的区别在于是否将新的 Promise 加入当前链。

总结

到此这篇关于JavaScript中Promise的执行顺序举例详析的文章就介绍到这了,更多相关JS Promise执行顺序内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章