使用 promises/dedelay 异步迭代一组文件



我的目标是遍历文件目录,对每个文件运行一些操作,并返回一个包含目录子集的 json 对象。

我使用 Node 的 fs 库中调用的同步版本让它工作,但我想找出最好的异步解决方案。我在异步版本的失败尝试下面是使用 Q 库来延迟。无论我做什么,我都无法在迭代完成之前推迟最后一步。

迭代成功完成,但不是在调用 sendResponse() 之前完成。

谁能帮助我了解我做错了什么?

router.get('/mediaTree', function(req, res){
  var mediaTree = { "identifier" : "id", "label" : "name", "items" : []}; 
  var idCounter = 1;
  var fs_readdir = q.denodeify(fs.readdir);
  fs_readdir(MEDIAPATH)
    .then(function(files) {
        files.forEach(function(dir) { 
          fs.stat(MEDIAPATH + dir, function(err, stats) {
            if(err) { console.log(err); return; }
            var thisFile = {};
            if(stats.isDirectory()) {
             thisFile.id = idCounter++;
             thisFile.type = "branch";  
             thisFile.name = dir;
             thisFile.path = MEDIAPATH + dir;
             mediaTree.items.push(thisFile);  
            } 
          });  
        }); 
    })
   .then(sendResponse);

  function sendResponse() {  
    res.json(mediaTree);
  }
});

为了使上面的代码正常工作,您必须充分利用承诺。请参阅MDN或类似来源,详细了解承诺如何运作。

鉴于此,您也应该将 fs.stat 包装在一个承诺中。这样,承诺可以为您管理等待结果,并为您提供在更同步的情况下运行大部分代码的选项。

var q = require('q'); // implied from your code sample
var path = require('path'); // joining paths with "+" might fail
router.get('/mediaTree', function(req, res){
    var qreaddir = q.denodeify(fs.readdir);
    var qstat = q.denodeify(fs.stat); 
    qreaddir(MEDIAPATH)
    .then(function(files) {
        // from inside out
        // - a promise for the fs.stat of a single file,
        // EDIT: Containing an object ob both the result and the dir
        // - an array of these promises created via Array.prototype.map from the files
        // - a promise from this array
        return q.all(files.map(function(dir) {
            return qstat(path.join(MEDIAPATH, dir))
                .then(function(stat) { // EDIT: extending the original answer
                    return {
                        dir: dir,   // the dir from the outer outer scope
                        stat: stat, // the stats result from the qstat promise
                    };
                });
        }));
    })
    .then(function(results) {
        // Promises should have no side effects, declare vars in function scope
        var mediaTree = {
            identifier: "id", 
            label: "name", 
            items: []
        };
        var idCounter = 1;
        // since we now have a sync array, we can continue as needed.
        results.forEach(function(result) {
            // EDIT: The original answer had the stats as immediate result
            // Now we get an object with both the dir and it's stat result.
            var stats = result.stats;
            var dir = result.dir; // Use this as needed.
            if (stats.isDirectory()) {
                var thisFile = {};
                thisFile.id = idCounter++;
                thisFile.type = "branch";  
                thisFile.name = dir;
                thisFile.path = path.join(MEDIAPATH, dir);
                mediaTree.items.push(thisFile);  
            }
        });
        return res.json(mediaTree);
    })
    .catch(function(err) {
        // Failsafe. Will log errors from all promises above. 
        console.log(err);
    });
});

相关内容

最新更新