Node.js 在forEach之后发送响应

Node.js 在forEach之后发送响应,node.js,foreach,fs,Node.js,Foreach,Fs,(请注意,这不是两个标题类似的问题的副本,这两个问题使用Mongoose,答案仅适用于Mongoose查询) 我有一个目录列表,每个目录都包含一个文件。我想返回一个JSON列表,其中包含每个文件的内容。我可以加载文件,没有问题,但是因为我使用forEach在数组上循环,所以在我实际加载文件内容之前会发送空响应: function getInputDirectories() { return fs.readdirSync(src_path).filter(function(file) {

(请注意,这不是两个标题类似的问题的副本,这两个问题使用Mongoose,答案仅适用于Mongoose查询)

我有一个目录列表,每个目录都包含一个文件。我想返回一个JSON列表,其中包含每个文件的内容。我可以加载文件,没有问题,但是因为我使用
forEach
在数组上循环,所以在我实际加载文件内容之前会发送空响应:

function getInputDirectories() {
    return fs.readdirSync(src_path).filter(function(file) {
        return fs.statSync(path.join(src_path, file)).isDirectory();
    });
}

router.get('/list', function(req, res, next) {
    var modules = [];
    var input_dirs = getInputDirectories();

    input_dirs.forEach(function(dir) {
        path = __dirname+'/../../modules/input/'+dir+'/module.json'
        fs.readFile(path, 'utf8', function(err, data) {
            modules.push(data);
        });
    });

    res.status(200).json(modules);
});

如何确保只在
模块
数组完全加载后才发送它,即:一旦
forEach
完成。

由于
fs.readFile
是异步的,因此您的行为很可能是预期的

您需要做的是在读取所有模块后返回模块。您可以在
fs.readFile
中执行此操作

据我所知,您可以通过
input\u dirs.length
获得目录总数(因为我猜
getInputDirectory()
正在返回一个数组)。现在,您需要某种计数器来帮助您了解是否已读取最后一个目录,如果已读取,则返回模块。像这样的方法应该会奏效:

router.get('/list', function(req, res, next) {
    var modules = [];
    var input_dirs = getInputDirectories();
    var c = 0;

    input_dirs.forEach(function(dir) {

        path = __dirname+'/../../modules/input/' + dir + '/module.json'

        fs.readFile(path, 'utf8', function(err, data) {
            c++;

            modules.push(data);

            if(c == input_dirs.length) {
                return res.status(200).json(modules);
            }
        });
    });

});

不要使用
fs.readFile
而是使用
fs.readFileSync

我建议您使用承诺,例如:

var Promise = require('bluebird');

router.get('/list', function(req, res, next) {
    var modules = [];
    var input_dirs = getInputDirectories();

    // 'each' will try to fulfill all promises, if one fails, it returns a
    // failed promise.
    return Promise.each(input_dirs, function(dir){
        path = __dirname+'/../../modules/input/'+dir+'/module.json';
        return new Promise(function(resolve, reject){
            fs.readFile(path, 'utf8', function(err, data) {
                if (err) return reject(err);
                return resolve(data);
            });
        });
    }).then(function(modules){
        return res.status(200).json(modules);
    })
    .catch(function(err){
        if (err) {
            //handle error
        }
    });

});

这样,一旦你履行了承诺,你就可以移动一个。

你试过使用吗?谢谢你的回答,你可以做到这一点。我相信你的意思是
c==input\u dirs.length
。另外,它的工作原理是在预期的时候发送响应。但是它抛出了一个
发送后无法设置头的
错误。感谢您的报告。检查一下,可能会有帮助。但是您在函数
getInputDirectory()
中到底在做什么呢?这可能是因为您已经将标题发送到了那里…所以我已经解决了问题。如果在
forEach
中的每次迭代中执行
console.log(c)
,您可以看到由于某种原因
c
总是
==inputs\u dirs.length
。我不明白为什么。我有两个目录,所以头被发送一次到第一个目录。如果您想查看这个问题,我已经用
getInputDirectory()
更新了这个问题。我想这是因为
readFile
也是异步的
forEach
readFile
完成之前循环。我将尝试同步readFileSync并返回给您。@有趣的是,您当时是如何解决问题的?好的,我想问题不在
getInputDirectories()
,而是您所描述的,即
c==inputs\u dirs.length
总是正确的。这可能也是因为正如您所说,
readFile
是异步的。可能会有一个解决方案(实际上不使用同步版本),我很确定…这不是一个好建议,IMO。在这种情况下,它可能会起作用,但一般来说,我们应该更喜欢异步函数而不是同步函数,以避免因为找不到新模块而阻塞接口…使用新模块(不是因为它不存在)在我看来,异步环境的解决方案似乎有点过分。事实上,Node完全是关于异步环境的,所以我总是鼓励人们学习原始技术来处理这个问题。是的,我同意你的看法。不过,有时你需要完成一些事情(特别是在工作中).最近这是我的经验,但你是对的,最好鼓励原始技术。问候!