如何在MongoDB服务层中使用async/await替代回调函数实现用户关联成就查询
问题描述
我正在基于MongoDB构建服务层,其中定义的User对象包含一个引用Achievements的数组。在完成用户身份验证后,我在服务层中编写了findForUser方法,通过回调函数查询指定用户关联的成就列表,代码如下:
findForUser(userId: string | Types.ObjectId): Promise<Achievement[]> { return new Promise((resolve) => { UserSchema.findOne({ _id: userId }, async (err: any, user: User) => { const AchievementMap: Achievement[] = []; if (err) throw new Error(err); user.achievements?.forEach((a) => { // @ts-ignore AchievementMap.push(a); }); resolve(AchievementMap); }); }); }
想请教如何用async/await替代当前的回调函数方式,实现返回UserSchema.findOne的查询结果?
解决方案
好问题!其实Mongoose的findOne方法本身就支持Promise接口,完全不需要手动包裹一层Promise再嵌套回调——用async/await可以让代码更简洁易读,同时错误处理也更规范。
改写步骤:
- 将方法标记为async:async方法会自动返回Promise,所以你不需要手动创建Promise实例
- 直接await findOne的结果:去掉回调参数,
findOne在没有回调时会返回Promise,直接用await接收查询到的user对象 - 规范错误处理:用try/catch捕获查询过程中可能出现的错误,替代回调里的err判断
- 简化成就数组的提取:不需要手动遍历push到新数组,直接返回
user.achievements(注意处理空值情况)
改写后的完整代码:
async findForUser(userId: string | Types.ObjectId): Promise<Achievement[]> { try { // 直接await findOne的Promise结果,不需要回调 const user = await UserSchema.findOne({ _id: userId }); // 处理用户不存在的情况(可选,但建议加上) if (!user) { throw new Error(`User with ID ${userId} not found`); } // 直接返回成就数组,空值时返回空数组避免undefined return user.achievements || []; } catch (err) { // 捕获并抛出错误,让上层调用者处理 throw new Error(`Failed to fetch achievements: ${(err as Error).message}`); } }
额外优化建议:
- 如果不需要Mongoose的文档实例(比如不需要调用save等方法),可以在findOne后加
.lean(),提升查询性能:const user = await UserSchema.findOne({ _id: userId }).lean(); - 如果你确定
user.achievements始终是数组(比如在Schema里设置了默认值为[]),可以直接返回user.achievements,不用加|| []
这样改写后,代码逻辑更清晰,错误处理也更符合async/await的最佳实践,同时避免了不必要的数组遍历和手动Promise包裹。
内容的提问来源于stack exchange,提问作者Ross Burchnall
相关产品推荐
相关产品推荐

