使用RxJs遍历node.js中的目录树

我正在尝试使用RxJs和node.js来遍历目录树。

我想出了工作解决scheme:

const filesInDir = Rx.Observable.fromNodeCallback(fs.readdir) const statFile = Rx.Observable.fromNodeCallback(fs.stat) const listFiles = (prefix, dir = '') => { const file$ = filesInDir(`${prefix}/${dir}`) .flatMap(file => file) .filter(file => !file.startsWith('.')) const isDir$ = file$ .map(file => statFile(`${prefix}/${dir}/${file}`)) .flatMap(file => file) .map(file => file.isDirectory()) return file$ .zip(isDir$, (file, isDir) => {return {file, isDir}}) .map(f => { if (f.isDir) { return listFiles(prefix, `${dir}/${f.file}`) } return Rx.Observable.return(`${dir}/${f.file}`) }) .flatMap(file => file) } listFiles('public') .toArray() .subscribe(list => { console.log(list) }) 

问题:

  1. 使用asynchronous操作是否有更高效/简洁的.map
  2. 同样的问题的.zip部分

伟大的问题。

我认为你可以做一些事情来优化这个查询。

首先,我们可以将map符后面的.flatMap(file => file)更改为一个flatMap。 微小的改进,但会运行更less的代码。

 const file$ = filesInDir(`${prefix}/${dir}`) .flatMap(file => file) .filter(file => !file.startsWith('.')) const isDir$ = file$ .flatMap(file => statFile(`${prefix}/${dir}/${file}`)) .map(file => file.isDirectory()) return file$ .zip(isDir$, (file, isDir) => {return {file, isDir}}) .flatMap(f => { if (f.isDir) { return listFiles(prefix, `${dir}/${f.file}`) } return Rx.Observable.return(`${dir}/${f.file}`) }) 

主要的改进是我相信你实际上打了两次文件系统。 filesInDir可观察序列不是热/caching序列。 如果是这样,目录树的recursion步行将不起作用。 考虑到这一点,您只需调用一次即可获取所有文件,然后再次调用isDirectory检查。 这引入了潜在的性能成本和bug。 您正在做出这样的假设,即当您点击磁盘时,返回的文件序列将始终以相同的顺序排列。 即使我们忽略了一下,那个磁盘是可变的,它可能会改变你的。 你可以保证,在asynchronous世界中,序列将以相同的顺序返回。 在我的机器上(Windows 10),序列大多以相同的顺序返回。 但是,如果树足够深(例如从_C:_),那么每次都会出现不匹配。

无论如何,性能修复也是错误修复。 而不是每次重新从文件系统读取,我们可以做一次。 将statFile()调用移入一个flatMap ,该flatMap也将结果映射到传递给statFile的文件的闭包

 const listFiles = (prefix, dir) => { return file$ = filesInDir(`${prefix}/${dir}`) .flatMap(file => file) .filter(file => !file.startsWith('.')) .flatMap(file => statFile(`${prefix}/${dir}/${file}`) .map( sf => {return {file, isDir: sf.isDirectory()}}) ) .flatMap(f => { if (f.isDir) { return listFiles(prefix, `${dir}/${f.file}`) } return Rx.Observable.return(`${dir}/${f.file}`) }) } 

这也有删除Zip子句的好处,因为我们不再试图使用两个序列。