使用promises / deferasynchronous迭代一组文件

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

我得到它在Node的fs库中使用同步版本的调用,但我想找出最好的asynchronous解决scheme。 我在一个asynchronous版本失败的尝试是使用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。 这样的承诺pipe理等待你的结果,并给你一个select运行在更多的同步问题的大部分代码。

 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); }); });