通过异步取消链接文件的文件夹非常慢

Unlinking a folder of files by async is super slow

我正在尝试调用一个 http 请求,该请求通过 fs 清空一个文件夹。我们假设没有子文件夹。因为forEach不是异步的,所以我选择使用asynceach而不是forEach。代码如下:

后端:

router.post('/emptyDir', function (req, res, next) {
    console.log("router.post /emptyDir");
    var dir = req.body.dir;
    var fs = require('fs');
    var async = require('async');
    fs.readdir(dir, function (err, files) {
        if (err) return console.log(err);
        console.log(JSON.stringify(files));
        async.each(files, function (file) {
            fs.unlink(dir + file, function (err) {
                if (err) return console.log(err);
                console.log(dir + file + " is removed");
            })
        }, function (err) {
            if (err) return console.log('A file failed to be removed');
            console.log('All files have been successfully removed');
            return res.json(dir);
        })
    });
});

前端:

this.rewriteAllFiles = function (files) {
    return $http.post('/emptyDir', { dir: prefix + idP + "/" })
        .then(function (res) {
            console.log("emptyDir finished");
            ...

我 运行 一个包含 2 个文件的文件夹的测试。它首先在浏览器日志中不显示任何内容,然后在后端显示以下日志:

router.post /emptyDir
["index.html","script.js"]
public/tmp/BGMTbU0RbeHdLAMjAAAD/index.html is removed
public/tmp/BGMTbU0RbeHdLAMjAAAD/script.js is removed

然后2分钟后,浏览器日志显示emptyDir finished,后台再次显示router.post /emptyDir

router.post /emptyDir
[]
All files have been successfully removed

我不明白为什么/emptyDir被调用了两次,为什么需要这么长时间...

所以有谁知道哪里错了吗?

编辑 1: 我意识到使用 Array.forEach 至少对我的测试有效。但我不确定它是否逻辑正确。 return files.forEach 完成之前可以使用以下函数吗?

router.post('/writeFiles', function (req, res, next) {
    console.log("router.post writeFiles");
    var dir = req.body.dir, files = req.body.files;
    var fs = require('fs');
    files.forEach(function (file) {
        fs.writeFile(dir + file.name, file.body, function (err) {
            if (err) return console.log(err);
            console.log(dir + file.name + " is written");
        })
    });
    console.log("All files have been successfully written");
    return res.json(dir);
})

Unlink 是异步的,所以你的整个 foreach 循环比你的代码的其余部分 运行 "at the same time" ].

我认为您可以考虑使用 Promises。您可以为每个取消链接的文件创建一个 Promise 数组。然后,您将使用 Promise.all().

等待所有 Promise 的完成(或失败)

类似的东西(它需要一些 "reorganisation" 但这是想法):

router.post('/emptyDir', function (req, res, next) {
    console.log("router.post /emptyDir");
    var dir = req.body.dir;
    var fs = require('fs');
    fs.readdir(dir, function (err, files) {
        if (err) return console.log(err);
        console.log(JSON.stringify(files));
        var unlinkQueue = files.map(function(file) {
            return new Promise(function(resolve, reject) {
                fs.unlink(dir + file, function (err) {
                    if (err) return reject(err);
                    console.log(dir + file + " is removed");
                    resolve(file);
                });
            });
        });
        Promise.all(unlinkQueue)
            .then(function(files) {
                console.log("All files have been successfully removed");
                res.json(dir);
            })
            .catch(function(err) {
                /* error */
            });
    });
}); 

我发现,相对于the doc of async,我忘记了async.each(files, function(file, callback) ...)中的callback。以下代码有效:

router.post('/emptyDir', function (req, res, next) {
    console.log("router.post /emptyDir");
    var dir = req.body.dir;
    var fs = require('fs');
    var async = require('async');
    fs.readdir(dir, function (err, files) {
        console.log(JSON.stringify(files));
        async.each(files, function (file, callback) {
            fs.unlink(dir + file, function (err) {
                if (err) {
                    console.log(err);
                    callback(err)
                } else {
                    console.log(dir + file + " is removed");
                    callback();
                }
            })
        }, function (err) {
            if (err) return console.log('A file failed to be removed');
            console.log('All files have been successfully removed');
            return res.json(dir);
        })
    });
});