【问题标题】:How to use an observable within `filter`?如何在“过滤器”中使用可观察对象?
【发布时间】:2018-12-23 01:36:07
【问题描述】:

假设我有一个类似于以下的目录结构:

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(子)子目录的子目录。我该怎么做?

(请注意,我要做的实际任务不是读取磁盘。我要做的是复杂的异步操作,我必须想出一个更简单的例子来说明我的问题)。

到目前为止我所尝试的:

我尝试使用map,但正如人们所期望的那样,它会发出一个 Observable 流:

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 来展平 Observables。这应该会产生三个布尔值,这些值最终会从这些 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!

【问题讨论】:

    标签: javascript node.js rxjs rxjs6


    【解决方案1】:

    你可以这样做:

    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 中的回调

    【讨论】:

    • 感谢您的回答。但这会产生一个布尔值作为结果。 (请参阅我的编辑)。哦,还有 rxviz 的 +1。
    • @Krumia 问题是fileExistsAndReadableRx 不会发出真/假,而是带有这些值的错误。我稍微更新了我的答案。你可能想更好地解决这个问题。
    • 我现在不在电脑前,但您可能希望使用cb(null, !err) 调用fileExistsAndReadableRx 中的回调而不是我编辑中的catchError
    • 我真是个白痴。就是这样,它是cb(null, !err),而不是cb(!err)。我希望你不介意我添加你的评论来回答,所以我可以接受。
    猜你喜欢
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 2023-01-10
    • 1970-01-01
    • 2018-07-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多