Node.js登录API如何等待异步函数返回值并正确响应
问题分析
你遇到的问题核心是异步操作的顺序问题:saveUserAccessDetails函数返回的是一个Promise(异步操作),但你在login函数里直接调用它时没有等待它执行完成,就立刻执行了res.sendResponse,这时候ObjId还没拿到,自然是undefined。
解决方案
我们需要调整代码的异步执行顺序,确保拿到saveUserAccessDetails的返回值后再发送响应,同时修正saveUserAccessDetails里的一个小bug(实例调用findOneAndUpdate的错误用法)。
具体修改步骤
- 在
login函数中等待saveUserAccessDetails执行完成:因为login已经是async函数,直接用await关键字等待Promise的结果。 - 调整响应发送时机:拿到
ObjId后再构造响应数据并调用res.sendResponse,避免提前发送响应。 - 修正
saveUserAccessDetails中的实例调用错误:findOneAndUpdate应该直接用模型UserAccessLogModel调用,而不是用new出来的实例userAccessModel。
修改后的完整代码
async function login (req, res, next) { try { const username = req.body.userName; const password = req.body.password; const otp = req.body.otp; const event = req.body.event; const Id = req.body.ObjId; const role = req.body.role; let data1 = {}; // 查找用户 const data = await UserModel.findOne({ username: username, status: "Active" }); if (!data || !pwdhash.verify(password, data['password'])) { res.sendResponse(403, false, null, 'Incorrect Username or password'); return reject('Incorrect Username or password'); } // 查找用户Token const usertokenDoc = await UserTokenModel.findOne({ UserId: data._id, Status: "Active", AccessToken: otp }); if (!usertokenDoc) { res.sendResponse(500, false, null, "OTP hasn't generated for this user."); return reject("OTP hasn't generated for this user."); } // 计算OTP有效期 const currentTime = new Date(); const tokenCreationTimeUtc = new Date(usertokenDoc.Date.toUTCString() + "UTC"); const difference = Math.max(0, currentTime - tokenCreationTimeUtc); const diffMinutes = Math.floor((difference / 1000) / 60); if (diffMinutes > 3) { res.sendResponse(500, false, null, "OTP timeout,regenerate the OTP."); return reject("OTP timeout,regenerate the OTP."); } // 生成JWT token const token = jwt.sign({ username: data['username'], user_id: data['_id'] }, config.secret, { expiresIn: config.tokenExpiry }); data1.email = data.email; data1.token = token; data1.username = data.username; data1.status = data.status; data1.role = data.role; data1.application = data.application; // 等待保存用户访问日志完成,拿到ObjId const ObjId = await saveUserAccessDetails(req, role, username, event, Id); // 发送响应 const successMsg = 'Authentication Successful...' + ObjId; res.sendResponse(200, true, data1, successMsg); resolve(successMsg); } catch (err) { res.sendResponse(500, false, null, err.toString()); reject(err); } } function saveUserAccessDetails(req, role, name, event, Id) { return new Promise(function (resolve, reject) { const channel = req.body.channel; const networkDetailsObj = req.body.networkDetails; if (event === "Logout") { // 注意:这里直接用模型调用findOneAndUpdate,而不是实例 UserAccessLogModel.findOneAndUpdate( { _id: Id }, { $addToSet: { logoutTime: req.body.logoutTime } }, // 修正:使用req.body.logoutTime,原代码未定义变量 { new: true }, function (err, accessDoc) { if (err) { return reject(err); } resolve(accessDoc._id); } ) } else { const userAccessModel = new UserAccessLogModel({ "user_name": name, "networkDetails": networkDetailsObj, "loggedIn_systemIp": req.body.loggedInSystemIp, "channel": req.body.channel, "loginTime": req.body.loginTime, "logoutTime": req.body.logoutTime, "name": name, "role": role, "Event_name": event }); userAccessModel.save(function (err, logDoc) { if (err) { console.log("error While saving access log" + err); return reject(err); } console.log("Access log saved successfully..." + logDoc._id); resolve(logDoc._id); }) } }) }
额外说明
- 我把原来的嵌套Promise改成了
async/await写法,这样代码更易读,也更容易处理异步顺序问题。 - 修正了
saveUserAccessDetails中logoutTime未定义的问题(原来直接用logoutTime,应该是req.body.logoutTime)。 - 所有的变量声明都改成了
const/let,避免使用var带来的变量提升问题。
内容的提问来源于stack exchange,提问作者Swapna
相关产品推荐
相关产品推荐

