Mongoose中countDocuments变量异常:为何文档name均为posts?
问题原因及解决办法
你遇到的问题核心是异步操作和变量作用域冲突导致的闭包陷阱。
因为MongoDB的findOne、create都是异步操作,会在循环完全执行结束后才开始执行回调。如果你的循环代码没有为每次迭代创建独立的作用域,所有异步回调都会共享同一个变量引用,等回调触发时,变量已经被循环更新到数组的最后一项(也就是posts),所以最终所有操作都用了posts这个值。
举个典型的错误写法例子:
// 错误写法:用var声明循环变量,导致作用域共享 for (var i = 0; i < collectionList.length; i++) { const item = collectionList[i]; Collection.findOne({ name: item.name }) .then(exists => { if (!exists) { return Collection.create({ name: item.name }); } }) .then(() => { console.log(item.name); // 最后全输出posts }); }
这里var i是函数级作用域,循环跑完后i变成2,item最后指向collectionList[1](也就是posts),所有异步回调都用了这个最终的item。
解决办法
有几种简单的修复方式:
- 用let声明循环变量,形成块级作用域,每次迭代都会创建独立的变量副本:
for (let i = 0; i < collectionList.length; i++) { const item = collectionList[i]; Collection.findOne({ name: item.name }) .then(exists => { if (!exists) { return Collection.create({ name: item.name }); } }) .then(() => { console.log(item.name); // 正常输出pages、posts }); }
- 使用forEach遍历,forEach的回调函数会为每个元素创建独立作用域:
collectionList.forEach(item => { Collection.findOne({ name: item.name }) .then(exists => { if (!exists) { return Collection.create({ name: item.name }); } }) .then(() => { console.log(item.name); }); });
- 用async/await配合for...of,把异步操作改成同步执行逻辑,彻底避开作用域问题:
async function initCollections() { for (const item of collectionList) { const exists = await Collection.findOne({ name: item.name }); if (!exists) { await Collection.create({ name: item.name }); } console.log(item.name); } } initCollections();
内容的提问来源于stack exchange,提问作者Caleb Gross
相关产品推荐
相关产品推荐

