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

Lambda嵌套Promise中bcrypt.compare返回pending的解决方法

问题场景
  • Lambda函数通过Mongoose连接MongoDB,接收前端传入的用户名、密码后,先查询匹配用户,再调用bcrypt比对加密存储的密码完成校验。
  • 代码运行时打印match变量得到{<promise pending>},由于密码校验逻辑写在Promise回调内部,无法直接添加await获取比对结果。

原问题代码:

require("dotenv").config();
const mongoose = require("mongoose");
const User = require('../server/models/user');
const bcrypt = require('bcryptjs')

exports.handler = async (event, context) => {
  context.callbackWaitsForEmptyEventLoop = false;
  
  mongoose.connect(`${process.env.MONGO_URI}`,{
    useNewUrlParser: true,
    useUnifiedTopology: true,
  }
  );
  
  let {name, password} = JSON.parse(event.body);

 let fd = await new Promise((res,rej)=>User.findOne(
    {
      name:name,
    },
    (err, user) => {
      if (err) {
        rej({
          statusCode: 500,
          body: JSON.stringify({ msg: err.message }),
        });
      } else {
    //==>    const match = await bcrypt.compare(password, JSON.stringify(user.password)); 

 
        if (match) {
        let statusCode = user && match? 200:405
        res({
          statusCode,
          headers: {
            "Access-Control-Allow-Origin": "*", 
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ user}),
        });
      }
      }
    }
  ));
  return fd
};
解决方案

不要混用Mongoose回调写法和Promise,直接用Mongoose原生支持的Promise API配合async/await写线性逻辑,从根源上避免回调嵌套导致的await无法使用问题,同时补全原代码的逻辑漏洞。

改写后的可运行代码:

require("dotenv").config();
const mongoose = require("mongoose");
const User = require('../server/models/user');
const bcrypt = require('bcryptjs')

// 缓存数据库连接,避免Lambda每次触发都新建连接
let dbConnection = null;
exports.handler = async (event, context) => {
  context.callbackWaitsForEmptyEventLoop = false;
  
  // 复用已有连接,降低冷启动耗时、减少MongoDB连接数压力
  if (!dbConnection) {
    dbConnection = await mongoose.connect(process.env.MONGO_URI, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    });
  }
  
  const { name, password } = JSON.parse(event.body);

  try {
    // 直接await查询结果,无需手动包裹Promise
    const user = await User.findOne({ name });
    // 补全用户不存在的分支逻辑
    if (!user) {
      return {
        statusCode: 404,
        headers: {
          "Access-Control-Allow-Origin": "*", 
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ msg: "用户不存在" })
      }
    }
    // 直接await bcrypt比对结果,注意不要给哈希值加JSON.stringify
    const isPasswordMatch = await bcrypt.compare(password, user.password);
    const statusCode = isPasswordMatch ? 200 : 405;
    
    return {
      statusCode,
      headers: {
        "Access-Control-Allow-Origin": "*", 
        "Content-Type": "application/json",
      },
      body: JSON.stringify(
        isPasswordMatch ? { user } : { msg: "密码错误" }
      )
    }
  } catch (err) {
    // 统一捕获查询、密码比对过程中的异常
    return {
      statusCode: 500,
      headers: {
        "Access-Control-Allow-Origin": "*", 
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ msg: err.message })
    }
  }
};
关键修改说明
  • 移除手动包裹的Promise层和Mongoose回调写法,直接await Mongoose查询返回的原生Promise,消除回调嵌套,bcrypt.compare可以直接在async函数流程中加await拿到布尔结果,不会出现pending状态。
  • 修复原代码的逻辑错误:去掉user.password外层多余的JSON.stringify,Mongoose查询返回的password字段本身就是字符串,转JSON会额外包裹引号导致密码比对永远失败;补全用户不存在、密码错误、运行时异常的分支处理,避免出现空指针报错、无返回值的问题。
  • 新增数据库连接缓存逻辑:Lambda执行环境会复用热实例,缓存连接可以避免每次触发函数都新建Mongo连接,大幅降低冷启动耗时,也不会打满MongoDB的最大连接数。

如果一定要保留原有Promise+回调的写法,只需要把new Promise传入的执行函数标记为async即可在回调内部正常使用await,但这种混用回调和Promise的写法维护成本高,非常不推荐。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 03:39:35