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

244 Views Asked by At

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

There are 1 best solutions below

0
On BEST ANSWER

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