我正在尝试编写一个函数来完成与使用带有promise模式的回调模式编写的相同的函数:
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);
});
});
});
};
此代码将遍历整个树,但它不会返回树,并且会发生通知操作,但延迟的promise不会解析。
在通知事件之前启动延迟承诺时,根本不会发生任何事情。
我知道我可以通过将done函数交给progress事件来解决这个问题,而不是试图给出某种类型的承诺,但是我想在这里充分利用promises,例如,代码完全符合我的要求:
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();
});
答案 0 :(得分:1)
我的第一个想法是简单地将原始函数包装在一个承诺中。这通常是我在不重新设计底层代码的情况下执行此操作的方式:
function readdirRecursiveWithPromise (path, handler) {
return new Promise((resolve, reject) => {
readdirRecursive(path, handler, (err, tree) => {
if (err) {
reject(err);
}
else {
resolve(tree);
}
});
})
}
不幸的是,当我尝试测试此代码时,我发现了一些与您的代码相关的潜在问题。
首先,我不知道你的'处理程序'应该做的。您尚未提供此说明或描述它应该做什么。它对问题至关重要,因为它控制着最终的回调是否最终被调用,所以我可以推测出这个“处理程序”是什么?控制此操作以及您的回调是否可以控制此操作。没有被称为可能是由于你的“处理程序”中的逻辑。
接下来的问题是你的待定'变量总共被设置为文件和目录的数量,但它仅针对目录递减。所以你的待定'变量永远不会达到0,并且永远不会调用调用回调的条件代码。
所以我要摆脱'处理程序'和'待定'而且我会告诉你我是如何用从头开始的承诺改写的。
以下是完整的工作代码示例:https://github.com/ashleydavis/read-directory-with-promises。继续阅读以获得解释。
让我们从基于承诺的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.
}
});
});
};
我们还需要一个基于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上找到了一个完整的工作示例:https://github.com/ashleydavis/read-directory-with-promises
希望它可以帮助你前进。