我试图使用RxJs和node.js遍历目录树。
我提出了工作解决方案:
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)
})
问题:
.map
使用异步操作是否有更有效/简洁的方法?.zip
部分答案 0 :(得分:5)
很棒的问题。
我认为你可以做一些事情来优化这个查询。
首先,我们可以将map
操作符后跟.flatMap(file => file)
更改为单个flatMap。微小的改进,但会运行更少的代码。
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
可观察序列不是热/缓存序列。
如果是,则目录树的递归遍历将不起作用。
考虑到这一点,您只需调用一次即可获取所有文件,然后再次调用它来执行isDirectory
检查。
这引入了潜在的性能成本和错误。
您假设当您点击磁盘时,返回的文件序列将始终处于相同的顺序。
即使我们忽略了一秒钟,该磁盘也是可变的,并且可能在您之下发生变化。
您可以保证在异步世界中序列将以相同的顺序返回。
在我的机器上(Windows 10),序列大多以相同的顺序返回。
但是如果有足够深的树(例如来自_C:_),我每次都会遇到不匹配。
无论如何,性能修复也是错误修复。
我们不是每次都从文件系统重新读取,而是可以执行一次。
将statFile()
调用移动到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
子句的好处,因为我们不再尝试使用两个序列。