如何在“过滤器”中使用可观察物?

时间:2018-07-15 06:49:56

标签: javascript node.js rxjs rxjs6

假设我的目录结构类似于以下内容:

foo
|
+---one
|   +---tmp
|
+---two
|
+---three
    |
    +---tmp

我想获取foo下的子目录列表,该目录下有一个tmp(子)子目录。我确实有以下代码可以做到这一点:

const { bindNodeCallback, from } = require('rxjs');
const { map, flatMap, filter } = require('rxjs/operators');
const { readdir, access, accessSync, constants: { F_OK, R_OK }} = require('fs');
const { join } = require('path');

const readdirRx = bindNodeCallback(readdir);

const FOO = './foo';

const result = readdirRx(FOO)
    .pipe(
        // readdir will return an array of files. Convert each
        // file into an observable, and then flatten it.
        flatMap(from),

        // get rid of directories that do not have a `tmp` dir underneath
        filter(dir => {
            try {
                accessSync(join(FOO, dir, 'tmp'), F_OK | R_OK);
                return true;
            } catch (err) {
                return false;
            }
        })
    );
result.subscribe(console.log, console.error, () => console.log('Done!'));
// outputs (correctly):
//   one
//   three
//   Done!

但是,这段代码对我来说很糟糕,因为(a)我在控制流中使用异常,并且(b)有一个同步的accessSync调用,这两者都对性能有不利影响。

我确实有以下反应式代码可以检查同一件事:

const fileExistsAndReadableRx = bindNodeCallback(
    // check if file exists and readable
    (file, cb) => access(file, F_OK | R_OK,
        // call the callback with true if no errors found
        err => cb(!err)
    )
);

但是,我不知道如何将fileExistsAndReadableRx插入上述程序。我的目标是删除try-catch块,并使用fileExistsAndReadableRx来过滤掉没有tmp(子)子目录的子目录。我该怎么办?

(请注意,我要执行的实际任务不是读取磁盘。我要执行的操作是一个复杂的异步操作,我不得不想出一个简单的示例来说明我的问题)。 / p>

到目前为止我已经尝试过:

我尝试使用map,但它发出了可观察到的流,就像人们期望的那样:

const result = readdirRx(FOO)
    .pipe(
        flatMap(from),
        map(dir =>
            fileExistsAndReadableRx(join(FOO, dir, 'tmp'))
        )
    );
result.subscribe(console.log, console.error, () => console.log('Done!'));
// Outputs:
//   Observable { _isScalar: false, _subscribe: [Function] }
//   Observable { _isScalar: false, _subscribe: [Function] }
//   Observable { _isScalar: false, _subscribe: [Function] }
//   Done!

所以我想,我知道!我将使用flatMap来使Observable扁平化。那应该产生三个布尔值,最终从那些Observables中发出。但这也不起作用。它只发出一个值:

const result = readdirRx(FOO)
    .pipe(
        flatMap(from),
        flatMap(dir =>
            fileExistsAndReadableRx(join(FOO, dir, 'tmp'))
        )
    );
result.subscribe(console.log, console.error, () => console.log('Done!'));
// Outputs:
//   true
//   Done!

编辑:

我尝试了@IngoBurkanswer,但是会产生一个布尔值。不是我期望的字符串列表:

const result = readdirRx(FOO)
    .pipe(
        flatMap(from),
        flatMap(dir => fileExistsAndReadableRx(join(FOO, dir, 'tmp'))
            .pipe(
                filter(Boolean),
                map(() => dir),
            )
        ),
    );
result.subscribe(console.log, console.error, () => console.log('Done!'));
// Outputs:
//   true
//   Done!

1 个答案:

答案 0 :(得分:2)

您可以执行以下操作:

readdirRx(FOO)
  .pipe(
    flatMap(from),
    flatMap(dir => fileExistsAndReadableRx(join(FOO, dir, 'tmp'))
      .pipe(
        catchError(res => of(res)),
        filter(Boolean),
        map(() => dir),
      )
    ),
  )

它假定fileExistsAndReadableRx返回Observable<boolean>filter(Boolean)只是filter(v => !!v)的缩写。

这里的技巧本质上就是您尝试过的方法,它是将每个目录(扁平化)映射到tmp文件夹的可观察检查中。然后,我们使用该结果过滤掉不匹配的结果,然后将其映射回目录,而不是中间结果。

您可以在此处查看它的运行情况:https://rxviz.com/v/d8djbkRO

  

您可能希望使用cb(null, !err)来调用fileExistsAndReadableRx中的回调