《JavaScript Promise 完全解析:executor、状态与链式调用》

164 阅读3分钟

深入理解 JavaScript Promise:从构造到状态流转

在现代 JavaScript 开发中,Promise 已成为处理异步操作的标准工具。但很多人只是“会用”,却不理解它背后的运行机制。本文将带你从零开始,深入剖析 Promise构造方式、内部状态变化规则,以及 .then().catch() 是如何协同工作的。


一、Promise 是如何构造的?

Promise 是一个内置的构造函数,通过 new Promise() 创建实例:

const myPromise = new Promise((resolve, reject) => {
  // executor(执行器)函数
});

构造函数接收一个参数:executor 函数

  • 这个函数会立即同步执行(不是异步!)。

  • Promise有三种状态

    • pending(待定)
    • fulfilled(已兑现)
    • rejected(已拒绝)
  • Promise两个内部属性

    • state
    • result
  • Promise接收两个参数:

    • resolve(value):用于将 Promise state变为 fulfilled(成功)、result:value

    • reject(error):用于将 Promise state变为 rejected(失败)、result:error

image.png

注意:即使你没有调用 resolvereject,Promise 也会一直处于 pending 状态,永远不会完成。

示例:基本构造

 console.log(1);
        //Promise 异步任务同步化
        //许诺 Promise 包含一个耗时性的任务
        const p=new Promise((resolve)=>{
            setTimeout(function() {
            console.log(2);
            resolve();
        }, 3000);
     
        })
        p.then(()=>{
            console.log(3);
        })
          console.log(4);

输出顺序:

image.png

1和4作为同步任务先执行
2在3秒后输出
当异步任务结束成功后.then处理函数输出3

这说明:executor 是同步执行的,但其中的异步逻辑(如 setTimeout)会被放入任务队列


二、Promise 状态的不可逆性

状态一旦改变,就不可逆转

const p = new Promise((resolve, reject) => {
  resolve('第一次 resolve');
  resolve('第二次 resolve'); // 无效!
  reject('尝试 reject');     // 也无效!
});

p.then(console.log); // 只输出 "第一次 resolve"

executor 只能调用一个 resolve 或一个 reject。任何状态的更改都是最终的。 所有其他的再对 resolve 和 reject 的调用都会被忽略:


三、.then():监听状态变化的桥梁

.then() 是 Promise 最核心的方法,用于注册成功和失败的回调函数

语法

promise.then(
  onFulfilled,   // 当状态变为 fulfilled 时调用
  onRejected     // 当状态变为 rejected 时调用(可选)
);

每次只根据异步处理结果运行一个函数

关键特性

  1. .then() 总是返回一个新的 Promise,支持链式调用。
  2. 如果 onFulfilled 返回一个值,新 Promise 会被 resolve 该值。
  3. 如果 onFulfilled 抛出异常,新 Promise 会被 reject

示例:成功与失败处理

const p = new Promise((resolve, reject) => {
  Math.random() > 0.5 ? resolve('OK') : reject(new Error('NO'));
});

p.then(
  result => console.log(' 成功:', result),
  error  => console.log(' 失败:', error.message)
);

提示:虽然 .then() 支持两个参数,但更推荐使用 .catch() 统一处理错误。


四、.catch():专为错误设计的处理器

.catch(onRejected).then(null, onRejected) 的语法糖。

promise
  .then(result => { /* 处理成功 */ })
  .catch(error => { /* 处理失败 */ });

为什么推荐用 .catch()

  • 统一错误捕获:链式调用中,任何一个环节抛出异常,都会被后续的 .catch() 捕获。
  • 避免遗漏错误处理:如果只用 .then() 的第二个参数,无法捕获 .then() 回调内部的错误。

对比示例

//  不推荐:无法捕获 then 内部的错误
promise.then(
  () => { throw new Error('Oops!'); },
  err => console.log('这里不会执行!')
);

//  推荐:能捕获所有前面的错误
promise
  .then(() => { throw new Error('Oops!'); })
  .catch(err => console.log('捕获到了:', err.message));

.catch其实是.then处理错误的一种简写方式.catch(f)调用是.then(null,f)的一种模拟


🔗 五、链式调用与状态传递

由于 .then().catch() 都返回新的 Promise,我们可以构建清晰的异步流程:

fetchUser()
  .then(user => fetchPosts(user.id))
  .then(posts => render(posts))
  .catch(err => showError(err));

在这个链条中:

  • 每一步的返回值会作为下一步的输入;
  • 任何一步出错,都会跳转到最近的 .catch()


总结:Promise 的核心心智模型

  1. 构造即执行new Promise(executor) 会立即运行 executor。
  2. 状态单向不可逆pending → fulfilledpending → rejected,仅一次。
  3. .then() 是观察者:根据状态决定调用哪个回调。
  4. .catch() 是安全网:集中处理整个链路的异常。
  5. 链式调用靠返回新 Promise:实现异步流程的线性表达。