Node.js异步函数返回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

