我想解决一个棘手的问题。为了说明这个问题,我将使用一个熟悉的场景:遍历目录。我知道有很多库已经遍历了一个目录。但是,这不是我想要做的。遍历目录只是我问题的一个隐喻。
基本上,我有以下内容:
structure: [],
traverseDirectory: function(path) {
var scope = this;
var promise = new Promise(function(resolve, reject) {
openDirectory(path, function(results) {
for (var i=0; i<results.length; i++) {
if (results[i].type === 'directory') {
scope.traverseDirectory(results[i].name);
} else {
scope.structure.push({ filename:name });
}
}
resolve(scope.structure);
});
});
return promise;
},
getDirectoryStructure: function(path) {
this.traverseDirectory(path)
.then(function(results) {
// Print out all of the files found in the directories.
console.log(JSON.stringify(results));
}
;
}
我的问题是在实际遍历目录之前.then
getDirectoryStructure
个{fire}。它不像我想象的那样等待。另外,我不知道如何通过目录结构来“传递”(不确定这是否是正确的词)的承诺。我甚至可以做我正在尝试承诺的事情吗?
感谢您的帮助。
答案 0 :(得分:1)
在这种情况下,您需要考虑每个级别有多个“步骤”...或者在您的目录遍历示例多个子目录中,所以基本上您需要分叉... @ Anonymous0day建议接近,但是退出for循环是反指示。
你需要的是Promise.all:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
var traverseDirectory = function(path) {
var promise = new Promise(function(resolve, reject) {
openDirectory(path, function(results) {
resolve(results);
});
});
return promise.then(function(results) {
var pros = [];
for (var i=0; i<results.length; i++) {
if (results[i].type === 'directory') {
pros.push(scope.traverseDirectory(results[i].name)); // <- recursive call
} else {
pros.push([{filename:name}]);
}
}
return Promise.all(pros).then(function(arrs) {
var structure = [];
for (var i=0; i<arrs.length; i++)
structure = structure.concat(arr[i]);
return structure;
});
});
}
(PS我有点“限制”这个,向你展示你不需要一个外部对象以同样的方式来跟踪结构...你可以将它保留在函数内部并且只在它暴露时外部的承诺解决了。)
但是你需要做的最重要的事情就是实际上等待解决外部承诺,直到你完成遍历(PS - 我会留给你看看Promise.all如果'pros'数组做什么是空的。
你看到它立即执行,因为它在完成for循环之后就完全解决了......如果那些递归实际上是异步的,那么事件循环确实会立即解决。
干杯,lemme知道这是否有意义。 [编辑正确的Promise.all()。然后(成功,失败)而不是我有的.catch。
答案 1 :(得分:1)
这是一种相对简洁的方法,可以避免for循环和变异变量。它返回所有检索结果的树结构:
// returns a promise for an object with two properties:
// directoryname (string)
// contents (array of objects (directories and files) for the contents of the directory)
function traverseDirectory(path) {
return new Promise(function(resolve, reject) {
openDirectory(path, resolve);
}).then(function (results) {
return Promise.all(results.map(function (item) {
return item.type === 'directory'
? traverseDirectory(item.name)
: { filename: item.name };
}));
}).then(function (contents) {
return {
directoryname: path,
contents: contents
};
});
}
如果您的目标是获取目录树中所有文件的平面数组,则可以执行此操作(除最后一个then
外,所有内容都相同):
// returns a promise for an array of all of the files in the directory and its descendants
function traverseDirectory(path) {
return new Promise(function(resolve, reject) {
openDirectory(path, resolve);
}).then(function (results) {
return Promise.all(results.map(function (item) {
return item.type === 'directory'
? traverseDirectory(item.name)
: { filename: item.name };
}));
}).then(function (contents) {
return Array.prototype.concat.apply([], contents);
});
}
答案 2 :(得分:1)
对于更灵活的方法,您可以选择:
.getDirectoryStructure()
提供它所说的内容,即目录层次结构的表示。首先,有几点要点:
openDirectory()
的版本。通过这样做,traverseDirectory()
中的代码将简化。 (在下面的解决方案中,traverseDirectory()
的剩余部分实际上归入getDirectoryStructure()
)。Promise.all()
汇总遍历期间生成的承诺系列。在执行此操作时,您可以省去外部变量structure
,而是依赖Promise.all()
来递送(递归)一系列结果。
以下是代码:
var dirOpener = {
openDirectoryAsync: function(path) {
return new Promise(function(resolve, reject) {
openDirectory(path, resolve);
});
},
getDirectoryStructure: function(path) {
var scope = this;
return scope.openDirectoryAsync(path).then(function(results) {
var promises = results.map(function(file) {
return (file.type === 'directory') ? scope.getDirectoryStructure(file.name) : { filename: file.name };
});
return Promise.all(promises);
});
},
flattenDeep: function(arr) {
var fn = arguments.callee;
return arr.reduce(function(a, x) {
return a.concat(Array.isArray(x) ? fn(x) : x);
}, []);
}
}
对于反映完整目录结构的数组,请调用如下:
dirOpener.getDirectoryStructure(rootPath)
.then(function(results) {
console.log(results);
})
.catch(function(e) {
console.log(e);
});
或者,对于仅包含文件名对象的扁平数组:
dirOpener.getDirectoryStructure(rootPath)
.then(dirOpener.flattenDeep)
.then(function(results) {
console.log(results);
})
.catch(function(e) {
console.log(e);
});