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

Node.js异步函数返回undefined:无法捕获返回的用户文档

问题:调用用户验证函数返回undefined

我需要检查数据库中是否存在用户文档、用户是否已完成验证,最终从函数返回该用户文档,但在另一个函数中无法捕获返回的用户文档,得到的结果始终是undefined。

返回用户文档的函数

const crypto = require('crypto');
const AppError = require('./AppError');
const catchAsync = require('./catchAsync');
const sendEmailVerificationToken = require('../utility/sendEmailVerification');

function generateToken() {
    const token = crypto.randomBytes(120).toString('hex');
    return token;
}

exports.createConfimationLinkAndSendEmail = (document) => {
    const token = generateToken();
    const confirmationLink = `localhost:3000/api/v1/verify/${token}`;
    sendEmailVerificationToken(
        confirmationLink,
        (document.email = 'Rohan@gmail.com')
    );
    return token;
};

exports.doUserExistAndNotVerified = catchAsync(async (userId, model, next) => {
    if (!userId) return next(new AppError('Please Enter USER ID'));
    const userDocument = await model.findById(userId);
    if (!userDocument) return next(new AppError('User Doesnot Exist'));
    if (userDocument.isVerified)
        return next(new AppError('Email is Already Verified'));

    return userDocument;
});

捕获返回值的函数

exports.confirmEmail = catchAsync(async (request, response, next) => {
    const { userId } = request.body;

    //CHECKING IF USER EXIST AND EMAIL IS  VERIFIED OR NOT
    const userDocument = await emailHandler.doUserExistAndNotVerified(
        userId,
        User,
        next
    );

    // FINDING TOKEN DOCUMENT BY HASH TOKEN AND USERID
    const hashedToken = crypto
        .createHash('sha256')
        .update(request.params.verificationToken)
        .digest('hex');

    const tokenDocument = await emailVerificationModel.findOne({
        owner: userId,
        token: hashedToken,
    });

    // CHECKING IF TOKEN DOCUMENT IS EMPTY
    if (!tokenDocument) return next(new AppError('Link Has Expired'));
    userDocument.isVerified = true;

    // CHANGING TOKEN AND USER DOCUMENT
    await userDocument.save({ validateBeforeSave: false });
    await emailVerificationModel.deleteOne({ owner: userDocument._id });

    // SENDING RESPONSE
    response.status(200).json({
        message: 'Email Verified Successfully',
    });
});

控制台打印userDocument结果为undefined。


问题原因及解决方案

核心问题

问题出在catchAsync的封装逻辑上。常见的ExpresscatchAsync是为路由处理函数(参数为req, res, next)设计的,它仅负责捕获异步错误并传递给next,不会返回异步函数的执行结果。而你的doUserExistAndNotVerified是普通异步函数(参数为userId, model, next),用这种catchAsync包裹后,调用时无法拿到函数的返回值,因此得到undefined。

解决方案1:修改catchAsync适配普通异步函数

调整catchAsync,让它能处理任意参数的异步函数,并返回Promise:

const catchAsync = fn => (...args) => {
  // 假设最后一个参数是next
  const next = args[args.length - 1];
  return fn(...args).catch(err => next(err));
};

修改后,doUserExistAndNotVerified的返回值会被正确传递,调用await就能拿到用户文档。

解决方案2:去掉catchAsync,手动处理错误

直接在doUserExistAndNotVerified内部用try/catch捕获错误并传递给next,同时确保正常流程返回用户文档:

exports.doUserExistAndNotVerified = async (userId, model, next) => {
  try {
    if (!userId) throw new AppError('Please Enter USER ID');
    const userDocument = await model.findById(userId);
    if (!userDocument) throw new AppError('User Doesnot Exist');
    if (userDocument.isVerified) throw new AppError('Email is Already Verified');

    return userDocument;
  } catch (err) {
    next(err);
    // 错误发生时返回undefined,调用方需要判断
  }
};

然后在调用处增加判断,避免后续代码因userDocument为undefined报错:

const userDocument = await emailHandler.doUserExistAndNotVerified(userId, User, next);
if (!userDocument) return; // 错误已通过next处理,直接返回终止函数

额外注意点

原代码中return next(new AppError(...))的写法,next函数通常返回undefined,即使不用catchAsync,这种写法也会让函数在错误分支返回undefined,但正常分支会返回用户文档,所以调用方需要判断是否拿到有效值。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 09:01:45