node.js循环中的异步函数?

时间:2013-10-28 17:01:34

标签: javascript node.js asynchronous foreach

我在node.js遇到了一些问题。我正在尝试做的是获取“./"+req.user.email中的一系列目录,并循环查看它们的大小并添加一个表格行输出,正如您在代码中看到的那样。最后,我不想使用res.send()发送所有表行。

然而,我得到的唯一输出是:

<tr></tr>

表示数组中的每个文件。似乎forEach函数根本不在等待readSizeRecursive。 readSizeRecursive函数是异步的,我相信这是导致问题的原因,但我不知道如何解决这个问题。

我将非常感谢任何帮助,我也包含了readSizeRecursive函数。谢谢!

  var output = "";
  fs.readdir("./" + req.user.email, function (err, files) {
    files.forEach(function(file){
      output += "<tr>";
      readSizeRecursive("./"+req.user.email+"/"+file, function (err, total){
        output += '<td>' + file + '</td><td>' + total + '</td>';
      });
      output += "</tr>"
    });
    res.send(output)
  });

readSizeRecursive():

// Function to find the size of a directory
function readSizeRecursive(item, cb) {
  fs.lstat(item, function(err, stats) {
    var total = stats.size;

    if (!err && stats.isDirectory()) {
      fs.readdir(item, function(err, list) {
        async.forEach(
          list,
          function(diritem, callback) {
            readSizeRecursive(path.join(item, diritem), function(err, size) {
              total += size;
              callback(err);
            }); 
          },  
          function(err) {
            cb(err, total);
          }   
        );  
      }); 
    }   
    else {
      cb(err, total);
    }   
  }); 
}

2 个答案:

答案 0 :(得分:0)

请使用async模块进行此类模式。使用async.each将允许您异步计算每个文件夹的大小,然后在完成单独计算所有内容后返回大小。

var output = [];

fs.readdir('./' + req.user.email, function (err, files) {
  async.each(compute, report);
});

function compute (file, done) {
  // calculate size, then callback to signal completion
  // produce a result like below, then invoke done()
  var obj = { files: [
    { name: file, size: size },
    { name: file, size: size },
    { name: file, size: size }
  ]};
  output.push(obj);
  done();
}

// doesn't need to be this awful
function format (list) {
  var result = [];

  list.forEach(function (item) {
    var description = item.files.map(function (file) {
      return util.format('<td>%s</td><td>%s</td>', file.name, file.size);
    });
    result.push(description);
  });

  result.unshift('<tr>');
  result.push('</tr>');
  return result.join('</tr><tr>');
}

function report (err) {
  if (err) { return next(err); }

  var result = format(output);
  res.send(result);
}

通过这种方式,您可以轻松地更换不同的功能,例如,在不改变文件大小树计算的情况下更改格式。

您的主要问题是控制流程。当您异步循环并确定大小时,您将返回res.send

答案 1 :(得分:-1)

var fs = require ("fs");

var createTableContent = function (p, cb){
    var read = function (p, cb){
        //Prevent recursion if error
        if (err) return cb ();

        fs.stat (p, function (error, stats){
            if (error){
                err = error;
                return cb ();
            }

            if (stats.isDirectory ()){
                var dirSize = 0;

                fs.readdir (p, function (error, entries){
                    if (error){
                        err = error;
                        return cb ();
                    }

                    var pending = entries.length;
                    //Empty dir
                    if (!pending) return cb (0);

                    entries.forEach (function (entry){
                        read (p + "/" + entry, function (entrySize){
                            dirSize += entrySize;
                            if (!--pending) return cb (dirSize);
                        });
                    });
                });
            }else{
                cb (stats.size);
            }
        });
    };

    //A lot of errors can be produced, return only the first one
    var err = null;

    //Suppose p is a dir
    fs.readdir (p, function (error, entries){
        if (error) return cb (error);

        var content = "";
        var pending = entries.length;
        if (!pending) return cb (null, content);

        entries.forEach (function (entry){
            read (p + "/" + entry, function (totalSize){
                if (err) return cb (err);
                content += "<tr><td>" + entry + "</td><td>" + totalSize + "</td></tr>";
                if (!--pending){
                    //End
                    cb (null, content);
                }
            });
        });
    });
};

//Here goes the "email" path
createTableContent (".", function (error, content){
    if (error) return console.error (error);

    console.log (content);
});