PAYATHON 2026

如何在 Promise 链最后的 .then 中访问 orderId 和 payment?

支付老李

结论

每个 .then() 回调的参数都是局部变量,只能在当前回调的作用域内使用。Promise 链每次只会把前一个回调的返回值传给下一个 .then(),不会自动保存之前出现的 orderId、payment 等值。

如果最后一个 .then() 需要同时访问 orderId、payment 和 remainingBalance,可以在链中持续传递一个包含这些数据的对象。还要注意,不要在链的中间用 .catch() 记录错误后就直接结束。这样会把失败状态转成成功状态,导致后续 .then() 继续执行。

为什么会出现 ReferenceError

下面的 payment 只在当前回调函数内有效:

.then((payment) => {
  console.log(`Payment of ${payment} has been received`);
  return payment;
})

这和普通函数参数一样:

function callback(payment) {
  return payment;
}

离开这个函数后,payment 就不在作用域内了。如果在另一个 .then() 中直接使用它:

.then((remainingBalance) => {
  console.log(payment); // ReferenceError
})

JavaScript 无法在当前作用域中找到 payment,于是抛出 ReferenceError: payment is not defined。

return payment 只会让紧接着的下一个 .then() 收到 payment。如果后续回调返回 UpdateWalletBalance(payment),Promise 链中继续传递的值就变成了这个 Promise 的结果,也就是 remainingBalance。Promise 不会替你保存之前的 payment。

推荐做法:在链中传递上下文对象

把需要保留的数据放进同一个对象,并在每一步返回更新后的对象:

const cart = ["shoes", "shirts", "boxers", "pants"];

function createOrder(cart) {
  return new Promise((resolve, reject) => {
    if (!validateCart(cart)) {
      reject(new Error("Cart is not valid"));
      return;
    }

    const orderId = "MK899";
    resolve(orderId);
  });
}

function proceedToPayment(orderId) {
  return new Promise((resolve, reject) => {
    if (!orderId) {
      reject(new Error(
        "Payment has failed. Please try again after some time"
      ));
      return;
    }

    const payment = "$500";
    resolve(payment);
  });
}

function updateWalletBalance(payment) {
  return new Promise((resolve, reject) => {
    if (!payment) {
      reject(new Error(
        "Balance did not change since payment failed"
      ));
      return;
    }

    const remainingBalance = "$100";
    resolve(remainingBalance);
  });
}

function validateCart() {
  return true;
}

createOrder(cart)
  .then((orderId) => {
    console.log(
      orderId,
      "OrderId verified. Please move on to the payment"
    );

    return { orderId };
  })
  .then(({ orderId }) => {
    return proceedToPayment(orderId).then((payment) => ({
      orderId,
      payment
    }));
  })
  .then(({ orderId, payment }) => {
    console.log(`Payment of ${payment} has been received`);

    return updateWalletBalance(payment).then((remainingBalance) => ({
      orderId,
      payment,
      remainingBalance
    }));
  })
  .then(({ orderId, payment, remainingBalance }) => {
    console.log(`Order ID: ${orderId}`);
    console.log(`Payment: ${payment}`);
    console.log(`Your remaining money is ${remainingBalance}`);
  })
  .catch((error) => {
    console.error(error.message);
  });

这样,最后一个 .then() 收到的是:

{
  orderId,
  payment,
  remainingBalance
}

因此它可以同时访问这三个值。

使用 async/await 可以写得更直观

如果没有必须使用 method chaining 的限制,async/await 更适合这种需要按顺序执行,并保留多个中间结果的流程:

async function checkout(cart) {
  try {
    const orderId = await createOrder(cart);

    console.log(
      orderId,
      "OrderId verified. Please move on to the payment"
    );

    const payment = await proceedToPayment(orderId);
    console.log(`Payment of ${payment} has been received`);

    const remainingBalance = await updateWalletBalance(payment);

    console.log(`Order ID: ${orderId}`);
    console.log(`Payment: ${payment}`);
    console.log(`Your remaining money is ${remainingBalance}`);
  } catch (error) {
    console.error(error.message);
  }
}

checkout(cart);

这三个变量都声明在 checkout() 的函数作用域中,后面的代码可以直接访问。

原代码中的其他问题

中间的 .catch() 会吞掉错误

原来的结构类似:

createOrder(cart)
  .then(...)
  .catch((error) => {
    console.log(error.message);
  })
  .then((orderId) => {
    return proceedToPayment(orderId);
  });

如果 createOrder() 失败,.catch() 只打印错误,没有 throw,也没有返回一个被拒绝的 Promise。此时 .catch() 返回的 Promise 会以 undefined 成功完成,所以下一个 .then() 仍会执行,并收到 undefined。

通常可以在链的末尾统一处理错误:

createOrder(cart)
  .then(...)
  .then(...)
  .then(...)
  .catch((error) => {
    console.error(error.message);
  });

如果需要在中间记录错误,同时让 Promise 保持拒绝状态,就重新抛出错误:

.catch((error) => {
  console.error(error.message);
  throw error;
})

reject() 后应立即结束执行

调用 reject() 不会像 return 那样退出 Promise executor:

if (!orderId) {
  reject(new Error("Payment failed"));
}

const payment = "$500";
resolve(payment);

Promise 的状态一旦确定,后面的 resolve() 不会再改变结果,但余下的代码仍会执行,可能产生不必要的副作用。应该写成:

if (!orderId) {
  reject(new Error("Payment failed"));
  return;
}

也可以直接写:

if (!orderId) {
  return reject(new Error("Payment failed"));
}

建议统一使用 Error 对象

不要混用字符串和 Error:

reject("Payment failed");

建议改成:

reject(new Error("Payment failed"));

这样,最终的错误处理就能稳定读取 error.message:

.catch((error) => {
  console.error(error.message);
});

Promise 链一次只会向后传递一个完成值。需要保留多个中间结果时,可以传递一个聚合对象,也可以改用 async/await,让这些变量处在同一个函数作用域中。

备注:内容仅供参考。