You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js Async/Await异步函数未完成即返回值问题排查

问题描述

我完全搞不懂自己在Async/Await概念上哪里出错了。以下是我的Node.js代码(分两个文件):
在execute.js中,console.log("hasInvoiceAlreadyBeenPaid:", hasInvoiceAlreadyBeenPaid);语句在controller.checkIfAuthNetChargedInvoice()执行完成前就运行了,输出的hasInvoiceAlreadyBeenPaid值为undefined。
我明明把函数定义为Async函数且使用了Await,对此十分困惑。需要说明的是,checkIfAuthNetChargedInvoice函数最终能成功运行,但日志中console.log("🎨 ~ ctrl:", ctrl);刚输出,紧接着就出现hasInvoiceAlreadyBeenPaid: undefined,之后该函数剩余逻辑才完成执行。我知道这是对async/await的理解误区,但到底哪里错了?

代码示例

execute.js

const controller = require("./controller");
const hasInvoiceAlreadyBeenPaid = await controller.checkIfAuthNetChargedInvoice(
  lastUnpaidSubscriptionInvoice,
  authorizeNetProfileId,
);
console.log("hasInvoiceAlreadyBeenPaid:", hasInvoiceAlreadyBeenPaid);
if (hasInvoiceAlreadyBeenPaid) {
  console.log("🎉 INVOICE ALREADY PAID! ");
}

controller.js

exports.checkIfAuthNetChargedInvoice = async (invoice, customerProfileId) => {
  try {
    const invoiceString = invoice._id.toString();
    const authNetInvoiceNumber = invoiceString.slice(-12);

    var merchantAuthenticationType = 
      new ApiContracts.MerchantAuthenticationType();
    merchantAuthenticationType.setName(authorizeNetAPILoginKey);
    merchantAuthenticationType.setTransactionKey(authorizeNetTransactionKey);

    var paging = new ApiContracts.Paging();
    paging.setLimit(100);
    paging.setOffset(1);

    var sorting = new ApiContracts.TransactionListSorting();
    sorting.setOrderBy(ApiContracts.TransactionListOrderFieldEnum.ID);
    sorting.setOrderDescending(true);

    var getRequest = new ApiContracts.GetTransactionListForCustomerRequest();
    getRequest.setMerchantAuthentication(merchantAuthenticationType);
    getRequest.setCustomerProfileId(customerProfileId);
    getRequest.setPaging(paging);
    getRequest.setSorting(sorting);

    var ctrl = new ApiControllers.GetTransactionListForCustomerController(
      getRequest.getJSON(),
    );
    console.log("🎨 ~ ctrl:", ctrl);

    ctrl.execute(async function () {
      var apiResponse = ctrl.getResponse();
      console.log("🎨 ~ apiResponse:", apiResponse);

      var response = new ApiContracts.GetTransactionListResponse(apiResponse);

      console.log(JSON.stringify(response, null, 2));

      if (response != null) {
        if (
          response.getMessages().getResultCode() == 
          ApiContracts.MessageTypeEnum.OK
        ) {
          console.log(
            "Message Code : " + 
              response.getMessages().getMessage()[0].getCode(),
          );
          console.log(
            "Message Text : " + 
              response.getMessages().getMessage()[0].getText(),
          );
          if (response.getTransactions() != null) {
            var transactions = response.getTransactions().getTransaction();
            for (var i = 0; i < transactions.length; i++) {
              const transaction = transactions[i];
              const transactionId = transaction.getTransId();
              const transactionStatus = transaction.getTransactionStatus();
              const settleAmount = transaction.getSettleAmount();

              if (
                transaction.invoiceNumber == authNetInvoiceNumber &&
                transactionStatus === "settledSuccessfully"
              ) {
                invoice.status = "paid";
                await invoice.save();
                return true;
              }
            }
          }
        } else {
          const newErrorLog = new ErrorLog({ error });
          await newErrorLog.save();
        }
      } else {
        let errorText = "checkIfAuthNetChargedInvoice: Null Response ";
        if (invoice._id) {
          errorText = 
            "checkIfAuthNetChargedInvoice: Null Response for Invoice: " + 
            invoice._id;
        }
        const newErrorLog = new ErrorLog({ error: errorText });
        await newErrorLog.save();
      }
    });
  } catch (error) {
    console.log("error", error);
    const newErrorLog = new ErrorLog({ error });
    await newErrorLog.save();
  }
};
问题原因与修复方案

核心问题

你给函数加了async关键字,但内部的ctrl.execute()是基于回调的异步操作,外层的async函数并没有等待这个回调执行完成就直接返回了——因为execute()不会返回Promise,外层的await根本等不到异步逻辑结束,函数默认返回undefined,所以才会出现日志顺序混乱、结果为undefined的情况。

另外,回调里的return true只是回调函数的返回值,不会传递给外层的checkIfAuthNetChargedInvoice函数。

修复步骤

要解决这个问题,需要把ctrl.execute()的回调逻辑包装成Promise,让async函数能等待这个异步操作完成后再返回结果:

修改后的controller.js代码

exports.checkIfAuthNetChargedInvoice = async (invoice, customerProfileId) => {
  try {
    const invoiceString = invoice._id.toString();
    const authNetInvoiceNumber = invoiceString.slice(-12);

    var merchantAuthenticationType = 
      new ApiContracts.MerchantAuthenticationType();
    merchantAuthenticationType.setName(authorizeNetAPILoginKey);
    merchantAuthenticationType.setTransactionKey(authorizeNetTransactionKey);

    var paging = new ApiContracts.Paging();
    paging.setLimit(100);
    paging.setOffset(1);

    var sorting = new ApiContracts.TransactionListSorting();
    sorting.setOrderBy(ApiContracts.TransactionListOrderFieldEnum.ID);
    sorting.setOrderDescending(true);

    var getRequest = new ApiContracts.GetTransactionListForCustomerRequest();
    getRequest.setMerchantAuthentication(merchantAuthenticationType);
    getRequest.setCustomerProfileId(customerProfileId);
    getRequest.setPaging(paging);
    getRequest.setSorting(sorting);

    var ctrl = new ApiControllers.GetTransactionListForCustomerController(
      getRequest.getJSON(),
    );
    console.log("🎨 ~ ctrl:", ctrl);

    // 用Promise包裹回调逻辑
    return new Promise(async (resolve, reject) => {
      ctrl.execute(async function () {
        try {
          var apiResponse = ctrl.getResponse();
          console.log("🎨 ~ apiResponse:", apiResponse);

          var response = new ApiContracts.GetTransactionListResponse(apiResponse);

          console.log(JSON.stringify(response, null, 2));

          if (response != null) {
            if (
              response.getMessages().getResultCode() == 
              ApiContracts.MessageTypeEnum.OK
            ) {
              console.log(
                "Message Code : " + 
                  response.getMessages().getMessage()[0].getCode(),
              );
              console.log(
                "Message Text : " + 
                  response.getMessages().getMessage()[0].getText(),
              );
              if (response.getTransactions() != null) {
                var transactions = response.getTransactions().getTransaction();
                for (var i = 0; i < transactions.length; i++) {
                  const transaction = transactions[i];
                  const transactionId = transaction.getTransId();
                  const transactionStatus = transaction.getTransactionStatus();
                  const settleAmount = transaction.getSettleAmount();

                  if (
                    transaction.invoiceNumber == authNetInvoiceNumber &&
                    transactionStatus === "settledSuccessfully"
                  ) {
                    invoice.status = "paid";
                    await invoice.save();
                    // 找到匹配交易,返回true
                    resolve(true);
                    return;
                  }
                }
              }
              // 遍历完未找到匹配交易,返回false
              resolve(false);
            } else {
              const newErrorLog = new ErrorLog({ error });
              await newErrorLog.save();
              resolve(false);
            }
          } else {
            let errorText = "checkIfAuthNetChargedInvoice: Null Response ";
            if (invoice._id) {
              errorText = 
                "checkIfAuthNetChargedInvoice: Null Response for Invoice: " + 
                invoice._id;
            }
            const newErrorLog = new ErrorLog({ error: errorText });
            await newErrorLog.save();
            resolve(false);
          }
        } catch (callbackError) {
          // 处理回调内部的异步错误
          console.log("callback error", callbackError);
          const newErrorLog = new ErrorLog({ error: callbackError });
          await newErrorLog.save();
          reject(callbackError);
        }
      });
    });
  } catch (error) {
    console.log("error", error);
    const newErrorLog = new ErrorLog({ error });
    await newErrorLog.save();
    return false;
  }
};

关键修改点

  • 用new Promise()包裹ctrl.execute()的回调逻辑,通过resolve()传递最终结果,reject()处理错误
  • 把回调里的return true替换为resolve(true),确保结果能传递到外层async函数
  • 遍历完交易未找到匹配项时,手动resolve(false),避免Promise一直处于pending状态
  • 给回调内部添加try/catch,处理invoice.save()这类异步操作的错误,避免静默失败

修改后,外层的await就能正确等待整个异步逻辑完成,hasInvoiceAlreadyBeenPaid会拿到正确的布尔值,日志顺序也会符合预期。

内容的提问来源于stack exchange,提问作者personwholikestocode

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 23:44:59