【问题标题】:Should an rxjs operator return a function or an observable?rxjs 操作符应该返回一个函数还是一个 observable?
【发布时间】:2020-05-22 10:47:30
【问题描述】:

我对 rxjs 中运算符的各种定义有点困惑。

下面我提供一些定义:

1 Pipeable Operator 是一个将 Observable 作为输入并返回另一个 Observable 的函数。
Creation Operators 是另一种运算符,可以作为独立函数调用以创建新的 Observable

2 运算符是一个函数,它将一个可观察对象(源)作为其第一个参数并返回另一个可观察对象(目标或外部可观察对象)

3 运算符采用配置选项,它们返回一个函数,该函数采用可观察的源。

4 运算符应该总是返回一个 Observable [..] 如果你创建的方法返回的不是 Observable,它就不是一个运算符,那就是很好。

由于 1,2,4 似乎与 3 相冲突,因此哪个定义是正确的。 rxjs 操作符有更好的定义吗?

例如:map。 map() 本身就是运营商吗?还是算子是map()的返回值?

【问题讨论】:

    标签: rxjs observable operators reactivex


    【解决方案1】:

    map() 本身是运算符吗?还是算子是map()的返回值?

    map() 的当前实现如下所示:

    export function map<T, R>(project: (value: T, index: number) => R, thisArg?: any): OperatorFunction<T, R> {
      return function mapOperation(source: Observable<T>): Observable<R> {
        if (typeof project !== 'function') {
          throw new TypeError('argument is not a function. Are you looking for `mapTo()`?');
        }
        return source.lift(new MapOperator(project, thisArg));
      };
    }
    

    所以,map() 是一个函数。它是 RxJS 术语中的运算符,是的,但它仍然是一个常规的 JavaScript 函数。就是这样。

    此运算符接收projection 回调函数,该回调函数由map 运算符调用。这个回调是你传递给map()的东西,例如value =&gt; value.id 来自这个例子:

    source$.pipe(map(value => value.id))
    

    map的返回值也是一个函数(声明为OperatorFunction&lt;T, R&gt;)。从mapreturnsfunction mapOperation()开始,你可以看出它是一个函数。

    现在,mapOperation 函数只接收一个参数:source,它的类型为 Observable&lt;T&gt;,并返回另一个(转换后的)Observable&lt;R&gt;。

    总结一下,当你说:

    Pipeable Operator 是一个将 Observable 作为其输入并返回另一个 Observable 的函数。

    这意味着当 RxJS 运算符(它是一个函数)将 Observable 作为其输入并返回另一个 Observable 时,它​​是可管道化的,在我们的例子中是这样:map 运算符确实返回一个函数 (mapOperation),其签名 ((source: Observable&lt;T&gt;): Observable&lt;R&gt;) 准确地表明:它接受一个 Observable 并返回另一个。

    运算符是一个函数,它将一个可观察对象(源)作为其第一个参数并返回另一个可观察对象(目标,或外部可观察对象)

    我已经多次提到运算符是一个公正的函数。

    操作员接受配置选项,并返回一个接受源可观察的函数。

    是的,在这种情况下,map() 可以被称为运算符,因为它接收配置选项 - projecttion 回调函数。所以,这里真的没有冲突,因为许多操作符都是可配置的。

    我会说这个有冲突:

    运算符应始终返回 Observable [..] 如果您创建的方法返回 Observable 以外的内容,则它不是运算符,这很好。

    我猜这是当可管道操作符不可管道时的旧定义。在引入 pipeable 操作符之前(我认为是在 RxJS 的第 5 版中),操作符返回的是 Observables。一个旧的map() 实现就表明了这一点。

    有关 RxJS 的创建者为何决定引入可管道操作符的更多信息,请查看this document。

    关于什么是 Observables 的另一篇精彩文章可以在 here 找到。

    还有:

    Creation Operators 是另一种运算符,可以作为独立函数调用以创建新的 Observable。

    of() 是一个创建运算符的示例,它返回(创建)一个 Observable。请看源代码。

    TL;DR:map() 是一个通常有一个参数(投影回调函数)的函数,它还返回一个函数,该函数接收源 Observable 并返回目标 Observable。

    编辑:为了回答您来自 cmets 的问题,我想在这里做。

    是的,在 RxJS 6 中,您可以创建一个函数来接受 observable 并返回另一个,这就是运算符。例如

    function myOperatorFunction(s: Observable<any>) {
      return of(typeof s);
    }
    

    你会这样称呼它

    source$.pipe(myOperatorFunction);
    

    请注意,我没有在pipe() 中调用myOperatorFunction,我只是传递了对它的引用,即我没有用括号写myOperatorFunction,但没有括号。那是因为pipe 接收函数。

    如果您需要传递一些数据或回调函数,例如 map 示例,您必须有另一个函数来接收您的参数,就像 map 接收 projection 参数一样,并且随心所欲地使用它。

    现在,您可能想知道为什么有些运算符不接收任何数据,但仍被创建为返回函数的函数,例如refCount()。这是为了与其他大多数具有一些参数的运算符相吻合,因此您不必记住哪些不接收参数或哪些具有默认参数(如min())。如果是refCount,如果写的和现在有点不同,你可以写

    source$.pipe(refCountOperatorFunction);
    

    而不是

    source$.pipe(refCount());
    

    但是你必须知道你必须这样写,所以这就是为什么函数被用来返回函数(接收可观察对象并返回另一个可观察对象)的原因。

    编辑 2:现在您知道内置运算符返回函数,您可以通过传入可观察的源代码调用它们。例如

    map(value => value.toString())(of())
    

    但这是丑陋的,不推荐管道操作员的方式,尽管它仍然可以工作。让我们看看它的实际效果:

    of(1, 2, true, false, {a: 'b'})
      .pipe(
        map(value => value.toString()),
        filter(value => value.endsWith('e'))
      ).subscribe(value => console.log(value));
    

    也可以这样写:

    filter((value: string) => value.endsWith('e'))(map(value => value.toString())(of(1, 2, true, false, {a: 'b'})))
      .subscribe(a => console.log(a));
    

    虽然这是一个完全有效的 RxJS 代码,但当您阅读后一个示例时,您无法想到它的作用。 pipe 在这里实际上做的是它 reduces over 传入的所有函数,并通过将之前的源 Observable 传递给当前函数来调用它们。

    【讨论】:

    • 那么,在rxjs6 中,我可以创建一个接受可观察对象并返回可观察对象并将其用作运算符的函数吗?或者我是否有义务创建一个返回函数的函数(接受 observable 并返回 observable)。
    • 我更新了我的答案,请查看@MarinosAn
    • 看起来有人(也许你是@MarinosAn?)创建了 GitHub 问题,要求解释 RxJS 和 Angular 项目中的第四句。
    【解决方案2】:

    是的。 map 本身就是一个运算符。

    每个操作符都会返回一个 Observable,以便稍后您可以订阅您创建的 Observable。

    当然,当我说“您创建”时,我的意思是您通过创建运算符或使用 Observable 类创建:new Observable

    可管道运算符只是一个将在管道实用程序函数中的运算符。

    任何返回带有 Observable 签名的函数的函数都可以通过管道传输,这就是为什么您可以创建自己的 Observables 并将操作符传递给它。

    我很确定您已经知道所有这些,而您只想知道为什么您正在阅读的内容存在冲突。

    首先,您不必向 Observable 传输任何内容。 你可以创建一个 Observable 并订阅它,就是这样。

    既然你不需要向 Observable 传递任何东西,我们应该如何处理操作符?

    它们是否只在管道中使用?

    答案是否

    如前所述,操作员采用配置选项,在您的示例中:

    const squareValues = map((val: number) => val * val);
    

    然后它返回一个新函数,该函数接受源 Observable,以便您以后可以订阅它:

    const squaredNums = squareValues(nums);
    

    正如我们所见,map 使用了(val: number) =&gt; val * val 并返回了一个函数,该函数现在获取了 Observable nums:const nums = of(1, 2, 3);

    在幕后ma​​p会根据配置获取源observable并进行变换。

    现在重要的是: 与其以这种方式(您可以但不需要那样)这样做,不如将您的运算符通过管道传递,这样您就可以将所有函数(假设您使用更多的运算符)组合成一个函数。

    同样,在幕后,您可以将管道称为一种更酷的方式来对源 Observable 进行操作,而不是声明许多变量,这些变量使用不同的操作符和一些配置,这些变量返回一个接受源 Observable 的函数。

    【讨论】:

      猜你喜欢
      • 2019-12-25
      • 1970-01-01
      • 2019-10-28
      • 2011-02-16
      • 2021-12-20
      • 2021-07-26
      • 2020-07-10
      • 2020-10-10
      • 1970-01-01
      相关资源
      最近更新 更多