我的目标是遍历文件目录,对每个文件运行一些操作,并返回包含目录子集的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);
}
});
答案 0 :(得分:1)
要使上述代码正常运行,您必须完全使用promises。有关承诺的详细信息,请参阅MDN或类似的来源。
鉴于此,你应该将fs.stat包装在一个promise中。这样,承诺管理等待您的结果,并为您提供以更加同步的方式运行大部分代码的选项。
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);
});
});