Cannot loop MongoDB Collection Functions

邮差的信 提交于 2019-12-23 19:20:10

问题


For some reason, I cannot loop collection.count. It keeps printing filename[5] 5 times instead of starting at 1 and going to 5.

This makes no sense because I can manually copy and paste this function 5 times and it will work, but when in a for loop, it doesn't.

for( var k =0; k<(filename.length)-2;k++) {

    collection.count( { "display.Name": filename[k] } , function(err, count) {
        console.log("Filename: " +filename[k]);
        console.log(" Trips: " + count);

    }); 

} 

NOTE: If I put the console.log outside of collection.count, I will see all 5 of my filenames. It is only within the specific function that it doesn't work.


回答1:


Let me try to explain this without spewing word soup and throwing out lots of fun terms like "asynchronous", "scope chain", or "closure".

The problem occurs because your loop finishes long before even the first MongoDB query returns. By the time your callback is running, the variable k has been through the loop 5 times and now equals 5. So by the time all five of your callbacks come back console.log("Filename: " + filename[k]); is going to be console.log("Filename: " + filename[5]); every single time.

The right queries are being made because a new query begins on each iteration of the loop and uses k while it's at the correct value; however, the variable k is defined in the scope above your callback so when the callbacks finally fire, k will be long finished with being incremented to 5.

Try this instead:

for( var k =0; k<(filename.length)-2;k++) {
    (function (k) {
        collection.count( { "display.Name": filename[k] } , function(err, count) {
            console.log("Filename: " +filename[k]);
            console.log(" Trips: " + count);
        }); 
    })(k);
}

Now it will work because we've created a new scope around the variable k. All that means in layman's terms is that technically you have two variables named k. The first one is the one you define in the for loop and the second one is the one that is created when the self-invoking function invokes itself, passing in k. The function's argument is also called k but it's technically a brand new variable definition. That means that the second k variable will NOT change on each iteration of the loop, only the one in the outer scope is changing.

A new anonymous function is defined on each iteration and k is passed into it. Now when the callbacks fire, each of their k variables will be unique and will not be the same as the outer k variable.

To make things easier to understand just change the variable name in the anonymous function so it's not the same as the outer one and then you'll see why this works.

for( var k =0; k<(filename.length)-2;k++) {
    (function (x) {
        collection.count( { "display.Name": filename[x] } , function(err, count) {
            console.log("Filename: " +filename[x]);
            console.log(" Trips: " + count);
        }); 
    })(k);
}

Another fancy way to accomplish the same thing is to only wrap the callback function in a new scope instead of the whole query. Though I'll leave it up to you to decide if it really is easier to read or not :P

for( var k =0; k<(filename.length)-2;k++) {
    collection.count( { "display.Name": filename[k] } , (function (x) {
        return function(err, count) {
            console.log("Filename: " +filename[x]);
            console.log(" Trips: " + count);
        };
    })(k)); 
}

In this one the self-invoking function is supplied as the second argument to collection.count. The self-invoking function is passed k, which becomes x inside the self-invoking function. It immediately returns another function; the function that will actually be passed as the second argument to collection.count. The self-invoking function becomes kind of a mini factory that returns another function which can now reference the variable defined in the outer scope (the scope inside the self-invoking function), which we know won't change because technically there is a new anonymous function (complete with x argument definition) being defined on every single iteration.

Put another way, I'm sure you understand that the callback function you defined is being defined five times; once for every iteration of the loop. Well the same goes for the anonymous self-invoking function. It too is created five separate times, including it's argument which is now locked in to whatever the value of k was at the time the function was invoked.

Hopefully that makes sense. It's not a complicated concept so much as it's complicated to try to explain clearly.



来源:https://stackoverflow.com/questions/21323904/cannot-loop-mongodb-collection-functions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!