fs.readdir 函数中的竞争条件在哪里?

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

我想列出目录中的所有文件夹,并在末尾列出有多少个文件夹。

这是我的代码:

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);
            }
        });
    }
});

代码应该:

  1. 阅读目录
  2. 目录中每个文件夹增加count
  3. 目录读取完成后,记录'folders: ' + count

在大多数情况下,这确实有效,我明白了:

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

虽然我有时会这样:

...
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

竞争条件发生在哪里?

我意识到竞争条件的发生是因为 i 可以在所有 fs.stat 完成执行之前完成递增,因为它在 fs.stats 的回调之外递增。

因此我需要一个单独的变量(j)来跟踪所有fs.stats的完成,并且只有当那些完成递增时才能我列出 count.

正确代码如下:

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);
            }
        });
    }
});

现在输出一致:

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