Javascript 如何处理使用承诺递归读取目录树
我正在尝试编写一个函数,该函数与使用带承诺模式的回调模式编写的以下函数相同:Javascript 如何处理使用承诺递归读取目录树,javascript,node.js,promise,q,Javascript,Node.js,Promise,Q,我正在尝试编写一个函数,该函数与使用带承诺模式的回调模式编写的以下函数相同: function readdirRecursive(path,handler,callback) { var errs = [], tree = {}; fs.readdir(path,function(err,dir) { if(err)return callback(err); var pending = dir.length; if(!pending)return
function readdirRecursive(path,handler,callback) {
var errs = [],
tree = {};
fs.readdir(path,function(err,dir) {
if(err)return callback(err);
var pending = dir.length;
if(!pending)return callback(null,tree);
dir.forEach(function(file) {
var newPath = Path.join(path,file);
fs.stat(newPath,function(err,stats) {
if(stats.isDirectory()) {
readdirRecursive(newPath,handler,function(err,subtree) {
tree[file] = subtree
handler(tree,newPath,file,"directory",function(err) {
if(err)errs.push(err);
if(!--pending)return callback(errs.length>0?errs:null,tree);
});
});
} else {
tree[file] = null;
handler(tree,newPath,file,"file",function(err) {
if(err)errs.push(err);
if(!--pending)return callback(errs.length>0?errs:null,tree);
});
}
});
});
});
};
这是我目前的尝试:
function readdirRecursive(path) {
var tree = {};
return Q.Promise(function(resolve,reject,notify) {
return readdir(path)
.then(function(dir) {
var futures = [];
var pending = dir.length;
if(!pending)return resolve(tree);
dir.forEach(function(file) {
var deferred = Q.defer();
var subPath = Path.join(path,file);
futures.push(stat(subPath)
.then(function(stats) {
if(stats.isDirectory()) {
tree[file] = tree;
var sub = readdirRecursive(subPath)
sub
.then(function(subtree) {
notify({
path:subPath,
name:file,
type:"directory",
done:deferred,
pending:pending
});
//return subtree;
},reject,notify);
} else {
tree[file] = null;
notify({
tree:tree,
path:subPath,
name:file,
type:"file",
done:deferred,
pending:pending
});
//return null;
}
//console.log("tree",tree);
deferred.promise()
.then(function() {
console.log("pending promise");
if(!--pending)resolve(tree);
}
,function(err) {
reject();
});
}));
});
return Q.all(futures)
.then(function(futures) {
console.log("hi",futures);
});
});
});
};
此代码将在整个树上迭代,但不会返回树,并且会发生通知操作,但延迟承诺永远无法解析
当延迟承诺在notify事件之前启动时,根本不会发生任何事情
我知道我可以通过将一个done函数交给progress事件来解决这个问题,而不是试图给出某种承诺,但我希望在这里尽可能充分地使用承诺,例如,这段代码正是我希望它做的:
function readdirRecursive(path) {
var tree = {};
return Q.Promise(function(resolve,reject,notify) {
return readdir(path)
.then(function(dir) {
var futures = [];
var pending = dir.length;
if(!pending)return resolve(tree);
dir.forEach(function(file) {
var deferred = Q.defer();
var subPath = Path.join(path,file);
console.log("file",file);
/*deferred.promise()
.then(function() {
console.log("pending promise");
if(!--pending)resolve(tree);
}
,function(err) {
reject();
});*/
futures.push(stat(subPath)
.then(function(stats) {
if(stats.isDirectory()) {
var sub = readdirRecursive(subPath)
sub
.then(function(subtree) {
tree[file] = subtree
notify({
path:subPath,
name:file,
type:"directory",
done:function(err) {
console.log("pending promise");
if(err)return reject(err);
if(!--pending)resolve(tree);
},
pending:pending
});
//return subtree;
},reject,notify);
} else {
tree[file] = null;
notify({
tree:tree,
path:subPath,
name:file,
type:"file",
done:function(err) {
console.log("pending promise");
if(err)return reject();
if(!--pending)resolve(tree);
},
pending:pending
});
//return null;
}
//console.log("tree",tree);
}));
});
return Q.all(futures)
.then(function(futures) {
console.log("hi",futures);
});
});
});
};
这是将执行这些函数的代码:
readdirRecursive("../").then(function(tree) {
console.log("TREE!!!",tree);
},function(err) {
console.log("ERROR",err);
},function(progress) {
console.log("PRGRESS WAS MADE",progress);
progress.done();
});
我的第一个想法是简单地将您的原始函数封装在一个承诺中。这通常是我在不重新设计底层代码的情况下执行此操作的方式:
function readdirRecursiveWithPromise (path, handler) {
return new Promise((resolve, reject) => {
readdirRecursive(path, handler, (err, tree) => {
if (err) {
reject(err);
}
else {
resolve(tree);
}
});
})
}
不幸的是,当我尝试测试这段代码时,我发现您的代码存在一些潜在的问题
首先,我不知道你的“处理者”应该做什么。您没有对此进行解释,也没有描述它应该做什么。这对问题很重要,因为它控制最终回调是否被调用,所以我可以推测“处理程序”控制着这个操作,如果你的“回调”没有被调用,可能是因为“处理程序”中的逻辑
下一个问题是,您的'pending'变量设置为文件和目录的总数,但它仅对目录递减。因此,您的“挂起”变量永远不会达到0,调用回调的条件代码也永远不会被调用
因此,我将去掉“handler”和“pending”,我将向您展示我如何从头开始用承诺重写这篇文章
下面是完整的工作代码示例:。请继续阅读以获得解释
让我们从基于承诺的readdir版本开始,该版本不是递归的:
function readdir (path) { // Promise-based version of readdir.
return new Promise((resolve, reject) => { // Wrap the underlying operation in a promise.
fs.readdir(path, (err, files) => {
if (err) {
reject(err); // On error, reject the promise.
}
else {
resolve(files); // On success, resolve the promise.
}
});
});
};
我们还需要一个基于承诺的函数,用于确定特定路径的类型(文件或目录):
function determineType (parentPath, childPath) { // Promise-based function to determine if the path is a file or directory.
return new Promise((resolve, reject) => {
fs.stat(path.join(parentPath, childPath), (err, stats) => {
if (err) {
reject(err);
}
else {
resolve({
path: childPath,
type: stats.isDirectory() ? 'directory' : 'file' // Check if it's a directory or a file.
});
}
});
});
};
现在我们可以展开determineType
并创建一个函数,该函数采用一个路径数组并确定每个路径的类型。这使用Promise.all
并行执行多个异步操作:
function determineTypes (parentPath, paths) { // Async function to determine if child paths are directories or files.
return Promise.all(
paths.map(
childPath => determineType(parentPath, childPath) // Is the path a directory or a file?
)
);
};
现在,我们可以构建基于承诺的重新发布版readdir
:
function readdirTree (rootPath) { // Read an entire directory tree, the promise-based recursive version.
return readdir(rootPath) // Initial non-recursive directory read.
.then(childPaths => determineTypes(rootPath, childPaths)) // Figure out the type of child paths.
.then(children => {
return Promise.all(children // Use Promise.all to figure out all sub-trees in a parallel.
.filter(child => child.type === 'directory') // Filter so we only directories are remaining.
.map(child => {
return readdirTree(path.join(rootPath, child.path)) // It's a directory, recurse to the next level down.
.then(subTree => {
return {
path: child.path,
subTree: subTree,
};
});
})
);
})
.then(children => {
const tree = {}; // Reorganise the list of directories into a tree.
children.forEach(directory => {
tree[directory.path] = directory.subTree;
});
return tree;
});
};
下面是一个使用示例:
readdirTree("c:\\some-directory")
.then(tree => {
console.log("tree:");
console.log(tree);
})
.catch(err => {
console.error("error:");
console.error(err);
});
我在Github中为您提供了一个完整的工作示例:
希望它能帮助您向前迈进。在bluebird wiki中有一个这样做的例子。我很好地阅读了目录,如果我想将它们读入一棵树,我可以这样做,但我想将它们读入一棵树,然后使用notify事件对它们中的每一个进行一些额外的处理。找到你的例子:我还必须使用Q,因此不推荐使用tag.progress。如果用不同的方式处理它会是最佳选择,尽管我对node/js非常熟练,但我对使用Promissions还是完全陌生。我已经用一些“最终目标”的工作示例更新了代码,删除了一个承诺,但是考虑到这一点,我真的不知道现在该怎么做,因为notify操作是我在这里追求的功能类型。作为堆栈溢出中最响亮的promise倡导者之一-我在这里不会使用它们-你想要流式处理多个结果,而不是一个-如果你想要流式处理它们,我会使用Observable;如果你想要使用promises,我会将它们推到数组中,然后
。所有阵列。