I'm new to Node.js. While i'm studying Node.js, i stucked at a point related to asynchronous/synchronous thing. I understand the logic of asynchronous functions and callbacks from other examples. However, i didn't understand the below example. I quoted it from : http://code-maven.com/list-content-of-directory-with-nodejs
Why doesn't the below code works properly? The author says it causes from the first parameter of fs.stat() method. The correct way of doing this is shown in the last two working examples. I don't understand the difference between the working and non-working examples and why the first example does not work properly and the last two examples works properly.
var fs = require('fs');
if (process.argv.length <= 2) {
console.log("Usage: " + __filename + " path/to/directory");
process.exit(-1);
}
var path = process.argv[2];
fs.readdir(path, function(err, items) {
for (var i=0; i<items.length; i++) {
var file = path + '/' + items[i];
console.log("Start: " + file);
fs.stat(file, function(err, stats) {
console.log(file);
console.log(stats["size"]);
});
}
});
The output is :
$ node examples/node/list_dir_direct.js ~/work/code-maven.com/examples/
Start: /home/gabor/work/code-maven.com/examples//blocking-read-file.js
Start: /home/gabor/work/code-maven.com/examples//node_hello_world.js
Start: /home/gabor/work/code-maven.com/examples//node_hello_world_port.js
Start: /home/gabor/work/code-maven.com/examples//non-blocking-read-file.js
Start: /home/gabor/work/code-maven.com/examples//process_exit.js
Start: /home/gabor/work/code-maven.com/examples//raw_command_line_arguments.js
Start: /home/gabor/work/code-maven.com/examples//read_dir.js
Start: /home/gabor/work/code-maven.com/examples//stats.js
/home/gabor/work/code-maven.com/examples//stats.js
97
/home/gabor/work/code-maven.com/examples//stats.js
243
/home/gabor/work/code-maven.com/examples//stats.js
270
/home/gabor/work/code-maven.com/examples//stats.js
151
/home/gabor/work/code-maven.com/examples//stats.js
18
/home/gabor/work/code-maven.com/examples//stats.js
324
/home/gabor/work/code-maven.com/examples//stats.js
27
/home/gabor/work/code-maven.com/examples//stats.js
1382
The debugging printout printed the names as expected, but then inside the callback of fs.stat() we keep printing out the same filename. comparing the results to the output of
$ ls -l ~/work/code-maven.com/examples/
total 64
-rw-r--r-- 1 gabor staff 97 Jan 29 14:26 blocking-read-file.js
-rw-r--r-- 1 gabor staff 243 Jan 27 12:34 node_hello_world.js
-rw-r--r-- 1 gabor staff 270 Jan 27 12:34 node_hello_world_port.js
-rw-r--r-- 1 gabor staff 151 Jan 29 14:26 non-blocking-read-file.js
-rw-r--r-- 1 gabor staff 18 Jan 31 08:24 process_exit.js
-rw-r--r-- 1 gabor staff 27 Jan 29 14:54 raw_command_line_arguments.js
-rw-r--r-- 1 gabor staff 324 Jan 31 15:26 read_dir.js
-rw-r--r-- 1 gabor staff 1382 Jan 31 10:45 stats.js
The sizes seem to match the filenames, because these were printed in the same order as we called fs.stat(), but for some reason the content of the file variable was the same for every callback. This happens because the file variable is just a simple global variable (from the point of view of the callback) and by the time the first callback was executed, the file variable was already assigned the last entry in the directory.
The below code works correctly.
var fs = require('fs');
if (process.argv.length <= 2) {
console.log("Usage: " + __filename + " path/to/directory");
process.exit(-1);
}
var path = process.argv[2];
fs.readdir(path, function(err, items) {
for (var i=0; i<items.length; i++) {
var file = path + '/' + items[i];
console.log("Start: " + file);
fs.stat(file, generate_callback(file));
}
});
function generate_callback(file) {
return function(err, stats) {
console.log(file);
console.log(stats["size"]);
}
};
Also, the below code works correctly, too.
var fs = require('fs');
if (process.argv.length <= 2) {
console.log("Usage: " + __filename + " path/to/directory");
process.exit(-1);
}
var path = process.argv[2];
fs.readdir(path, function(err, items) {
for (var i=0; i<items.length; i++) {
var file = path + '/' + items[i];
console.log("Start: " + file);
fs.stat(file, function(f) {
return function(err, stats) {
console.log(f);
console.log(stats["size"]);
}
}(file));
}
});