使用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) })
问题:
- 使用asynchronous操作是否有更高效/简洁的
.map
? - 同样的问题的
.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
子句的好处,因为我们不再试图使用两个序列。