MongoDB查询后数组通过索引访问返回undefined问题求助
我写了一段代码用来统计MongoDB中一周内每天的文档数量,最终把结果放到qweek数组里。代码如下:
function dates(current) { var week = new Array(); // Starting Monday not Sunday current.setDate((current.getDate() - current.getDay() + 1)); for (var i = 0; i < 7; i++) { var dd = String(current.getDate()).padStart(2, '0'); var mm = String(current.getMonth() + 1).padStart(2, '0'); //January is 0! var yyyy = current.getFullYear(); var day = dd + '/' + mm + '/' + yyyy; week.push(day); current.setDate(current.getDate() + 1); } return week; } // Initialize the App Client const client = stitch.Stitch.initializeDefaultAppClient("app-id"); // Get a MongoDB Service Client const mongodb = client.getServiceClient( stitch.RemoteMongoClient.factory, "mongodb-atlas" ); //projection config const options = { // Match the shape of RemoteFindOptions. limit: 1000, // Return only first ten results. projection: { // Return only the `title`, `releaseDate`, and day: 1, // (implicitly) the `_id` fields. }, sort: { // Sort by releaseDate descending (latest first). releaseDate: -1, }, } // Get a reference to the travels database const db = mongodb.db("travels"); function displayCountTravels() { var daysweek = dates(new Date()); var qweek = new Array(); for (var l = 0; l < daysweek.length; l++) { db.collection("details") .find({ "day": daysweek[l] }, options) .toArray() .then(docs => { qweek.push(docs.length); }); } console.log(qweek); console.log(qweek[1]); return qweek; }
执行后,用console.log(qweek)能看到数组已经填充了对应数值:
Log output:[]
0: 0
1: 0
2: 0
3: 2
4: 0
5: 0
6: 0
length: 7
proto: Array(0)
但通过索引访问元素,比如console.log(qweek[1])却返回undefined,这是为什么?
问题根源:异步操作的时序坑
这是JavaScript里非常常见的异步操作问题!当你执行到console.log(qweek)和console.log(qweek[1])时,那些MongoDB的查询请求还在后台跑呢——find().toArray()返回的是一个Promise,它不会阻塞主线程,代码会继续往下执行。
所以当时打印qweek的时候,它确实是空数组[],但浏览器控制台有个"小特性":它会实时更新引用类型的显示内容。等后面异步请求完成,then回调把数据push到qweek后,控制台里的数组就跟着更新了,这才让你误以为打印的时候数组已经有值。但实际上,执行console.log(qweek[1])的那一刻,数组里还没有任何元素,所以返回undefined。
解决方案:等待所有异步操作完成
要解决这个问题,你需要确保在访问qweek的元素之前,所有的MongoDB查询都已经完成。这里有两种常用的方法:
方法1:使用async/await重构函数
把displayCountTravels改成异步函数,逐个等待每个查询完成:
async function displayCountTravels() { var daysweek = dates(new Date()); var qweek = new Array(); for (var l = 0; l < daysweek.length; l++) { const docs = await db.collection("details") .find({ "day": daysweek[l] }, options) .toArray(); qweek.push(docs.length); } console.log(qweek); console.log(qweek[1]); // 现在能正确获取值了 return qweek; }
注意:调用这个异步函数时,需要用await或者.then()来获取返回的qweek数组。
方法2:使用Promise.all并行处理所有查询
如果想让所有查询并行执行(提高效率),可以把所有Promise收集起来,用Promise.all等待全部完成:
function displayCountTravels() { var daysweek = dates(new Date()); // 收集所有查询的Promise const countPromises = daysweek.map(day => { return db.collection("details") .find({ "day": day }, options) .toArray() .then(docs => docs.length); }); // 等待所有Promise完成 return Promise.all(countPromises).then(qweek => { console.log(qweek); console.log(qweek[1]); // 这里能正确获取值 return qweek; }); }
这种方法的优势是所有查询同时发起,比逐个等待更快,适合处理多个独立的异步请求。
内容的提问来源于stack exchange,提问作者João Gabriel Araujo

