Return 遍历文件夹函数的最终值

Return final value from traversing folder function

问题:为什么 var things return 不是来自 walk() 函数之外的值?我该如何解决?

假设:这是异步的,console.log 发生得太早了。这将引导我如何使它成为 Promise(我正在使用节点 4.1.1)

var walk = function(dir, done) {
  var results = [];
  fs.readdir(dir, function(err, list) {
    if (err) return done(err);
    var i = 0;
    (function next() {
      var file = list[i++];
      if (!file) return done(null, results);
      file = dir + '/' + file;
      fs.stat(file, function(err, stat) {
        if (stat && stat.isDirectory()) {
          walk(file, function(err, res) {
            results = results.concat(res);
            next();
          });
        } else {
          results.push(file);
          next();
        }
      });
    })();
  });
};

function traverseDirectories() {
    var things = walk('src/', function(err, results){
        if(err) throw err;
        console.log(results) // ['dir/thing.png', 'dir/thing2.png']
        return results;
    });
    console.log(things) // undefined
};
traverseDirectories();

Q. Why won't a var things return a value from outside the walk() function?

R。因为 walk 没有 return 任何东西(看一看,你会发现它是一个 void 函数)。


即使你把它变成一个 Promise,你也不能像这样使用它:

var things = walk(...);
console.log(things);

因为 Promise 是可行的,并且仍然是异步的,所以它将是:

walk(...).then(function(things) {
  // do something with things here
});

为了做你想做的事,你需要一些当前 Javascript 中不存在的东西。

有一个ES7 proposal of native async/await将是回调天堂,但是atm,你可以使用:

  • Async/Await library(这是一个了不起的库,但与原生相差甚远,性能也不酷)
  • ES7 转译器 - 您今天可以编写 ES7 代码,它会为您转译为 ES5(例如 Babel

但是,如果您已经在使用最新版本的 NodeJS(撰写本文时为 4.0.0)——如果您还没有,那么您真的应该——实现您想要的最好方法是使用 生成器.

结合一个名为co的小库,它会帮助你实现几乎ES7async/await所提出的,而且它会主要使用native代码,所以可读性和性能都非常好:

var co = require('co');

var traverseDirectories = co(function *traverseDirectories() {
  var things = yield walk('src/');
  console.log(things) // there we go!
});

function walk(dir, results) {
  return new Promise(function(resolve, reject) {
    fs.readdir(dir, function(err, list) {
      if (err)
        reject(err);

      var i = 0;
      (function next() {
        var file = list[i++];
        if (!file) resolve(results);
        file = dir + '/' + file;
        fs.stat(file, function(err, stat) {
          if (stat && stat.isDirectory()) {
            walk(file).then(function(res) {
              results = results.concat(res); 
              next();
            });
          } else {
            results.push(file);
            next();
          }
        });
      })();
    });
  });
}

您可以阅读有关此主题的更多信息in this awesome Thomas Hunter's blog post