我的目标是遍历文件目录,对每个文件运行一些操作,并返回一个包含目录子集的 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);
});
});