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

Node.js登录API如何等待异步函数返回值并正确响应

问题分析

你遇到的问题核心是异步操作的顺序问题:saveUserAccessDetails函数返回的是一个Promise(异步操作),但你在login函数里直接调用它时没有等待它执行完成,就立刻执行了res.sendResponse,这时候ObjId还没拿到,自然是undefined。

解决方案

我们需要调整代码的异步执行顺序,确保拿到saveUserAccessDetails的返回值后再发送响应,同时修正saveUserAccessDetails里的一个小bug(实例调用findOneAndUpdate的错误用法)。

具体修改步骤

  1. 在login函数中等待saveUserAccessDetails执行完成:因为login已经是async函数,直接用await关键字等待Promise的结果。
  2. 调整响应发送时机:拿到ObjId后再构造响应数据并调用res.sendResponse,避免提前发送响应。
  3. 修正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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:05:38