node.js - Where is the race condition in this fs.readdir function?

I would like to list all folders in a directory and at the end list how many folders there are.

This is my code:

fs.readdir(dir, (err, folders) => {
    if (err) return console.log(err);

    let count = 0;
    for (let i = 0; i < folders.length; i++) {
        let folder = folders[i];

        fs.stat(dir + '/' + folder, (err, stats) => {
            if (err) return console.log(err);

            if (stats.isDirectory()) {
                console.log(folder);
                count++;
            }
            if (i >= (folders.length - 1)) {
                console.log('folders: ' + count);
            }
        });
    }
});

The code should:

  1. Read the directory
  2. Increment count for each folder in the directory
  3. When the directory read has finished, log 'folders: ' + count

In most cases this does work and I get this:

...
2016-12-20--09-59-12
2016-12-20--09-59-13
2016-12-20--09-59-14
folders: 86

Sometimes though I get this:

...
2016-12-20--09-59-12
2016-12-20--09-59-11
2016-12-20--09-59-14
folders: 85
2016-12-20--09-59-13

Where is the race condition happening?

1 Answer

  1. Leander- Reply

    2019-11-15

    I realised that the race condition happens because i can finish incrementing before all the fs.stats have finished executing, because it increments outside of fs.stats's callback.

    I therefore need a separate variable (j) to keep track of all the fs.statss completions, and only when those have finished incrementing can I list count.

    Here is the correct code:

    fs.readdir(dir, (err, folders) => {
        if (err) return console.log(err);
    
        let count = 0,
            j = 0; // this bad boy!
    
        for (let i = 0; i < folders.length; i++) {
            let folder = folders[i];
    
            fs.stat(dir + '/' + folder, (err, stats) => {
                if (err) return console.log(err);
                j++; // j, unlike i, only gets incremented *inside* the async function
    
                if (stats.isDirectory()) {
                    console.log(folder);
                    count++;
                }
                if (j >= folders.length) { // check j, not i
                    console.log('folders: ' + count);
                }
            });
        }
    });
    

    Now the output is consistently:

    ...
    2016-12-20--09-59-13
    2016-12-20--09-59-11
    2016-12-20--09-59-14
    folders: 86
    

Leave a Reply

Your email address will not be published. Required fields are marked *

You can use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>