使用await保存时Mongoose post hook无响应问题排查
Mongoose 8.0.0 Post-Save钩子中await doc.save()导致请求停滞的原因
问题核心
你遇到的请求停滞,本质是无限递归循环引发的:在post-save钩子中调用await doc.save(),会再次触发同一个post-save钩子,形成无限调用,卡死Node.js事件循环,导致路由里的await postNew.save()永远无法完成,请求自然挂起无响应。
流程拆解
- 路由中执行
await postNew.save(),触发第一次post-save钩子 - 钩子内生成slug后调用
await doc.save(),发起第二次保存操作,这会再次触发post-save钩子 - 第二次钩子又会执行
await doc.save(),触发第三次钩子……循环无限持续 - 整个循环占用事件循环资源,路由里的初始save操作永远等不到结束,请求就一直停滞
为什么去掉await或用.then()能正常工作?
- 去掉
await时,doc.save()异步执行,钩子直接调用next(),让路由里的save流程继续走完并响应请求。但这种做法有隐患:后续的save操作可能失败但无法捕获处理,会导致数据不一致。 - 用
.then()时,你在save的回调里调用next(),让路由流程先响应请求。但递归仍在后台运行,同样可能引发性能问题或数据异常。
为什么有些资料里用await没问题?
那些场景不是在post-save钩子内调用save:
- 比如在
pre-save钩子中使用:pre-save是在保存前执行,修改文档后不需要手动调用save,Mongoose会自动把修改后的文档存入数据库,不会触发循环。 - 或者是在其他非save触发的方法/钩子中调用save,不存在递归触发的问题。
正确解决方案
方案1:改用pre-save钩子(推荐)
在保存前生成slug,不需要额外调用save,从根源避免循环:
PostsSchema.pre("save", async function(next) { try { // 仅当postTitle被修改或文档是新创建时才生成slug,避免重复操作 if (this.isModified("postTitle") || this.isNew) { this.postSlug = slugify(this.postTitle, { lower: true }); } next(); } catch (err) { next(createError(500, err.message)); } });
方案2:在post-save钩子中用updateOne替代save
如果必须在保存后操作,用updateOne更新slug,不会触发save钩子:
PostsSchema.post("save", async function(doc, next) { try { const slug = slugify(doc.postTitle, { lower: true }); await PostsModel.updateOne({ _id: doc._id }, { $set: { postSlug: slug } }); next(); } catch (err) { next(createError(500, err.message)); } });
内容的提问来源于stack exchange,提问作者I do have a mental disability
相关产品推荐
相关产品推荐

