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

我正在尝试使用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部分的同样问题

解决方法:

好问题.

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

首先,我们可以将.flatMap(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中,该map还将结果映射到传递给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子句的好处,因为我们不再尝试使用两个序列.

上一篇:javascript – 一个Observable,多个订阅者,不同的案例,使用rxjs5的不同share()行为


下一篇:javascript – 在RxJS中链接多个flatMap运算符时,如何摆脱Bluebird警告?