阅读 1756

RxJS 5.5: 即将到来的 pipeline

RxJS 5.5 在上周已经发布了 beta.7 版本,在 5.5 中 RxJS 引入了 lettable operator 这一个新特性。依靠这个特性,RxJS 中的流与操作符可以用更加 FP 的风格组合起来。

lettable operator 试图解决的问题

在之前版本的 RxJS 5 中,操作符通过 dot chaining 的方式进行组合。在考虑 bundle size 的场合,RxJS 的操作符往往以

  • import "rxjs/add/operator/SOME_OPERATOR" (instace operator)
  • import "rxjs/add/observable/SOME_OPERATOR" (static operator)

的方式加入应用程序中。通过这种方法,避免了把完整的 RxJS 全部打包。

这种 rxjs/add 风格的引入,相当于在 RxJS 库外动态地将操作符加入 ObservableObservable.prototype 当中。这也就使得 RxJS 几乎不能受益于 webpack 2 或者 rollup 的 tree-shaking

此外,如果没有使用引入的操作符,TypeScript、Flow 或者各种 linter 是不会因此报错的。考虑到 RxJS 中琳琅满目的操作符,很多时候会在编码过程中不断更换操作符,有时候是会忘记删除引入的操作符,导致 bundle size 的增加。

lettable operator 的使用方法

lettable operator 这个名字是 RxJS 社区中的黑话,RxJS 社区中有 issue 在讨论是不是要改一个对初学者更友好的名字。

lettable operator 需要配合 Observable.prototype.pipe 一起使用,下面来看一个使用 lettable operator 的例子:

import { range } from 'rxjs/observable/range';
import { map, filter, scan } from 'rxjs/operators';

const source$ = range(0, 10);

source$.pipe(
  filter(x => x % 2 === 0),
  map(x => x + x),
  scan((acc, x) => acc + x, 0)
)
.subscribe(x => console.log(x))复制代码

相对的,dot chaining 风格的写法如下:

import { Observable } from "rxjs/Observable";
import "rxjs/add/observable/range";
import "rxjs/add/operator/map";
import "rxjs/add/operator/filter";
import "rxjs/add/operator/scan";

const source$ = Observable.range(0, 10);

source$
  .filter(x => x % 2 === 0)
  .map(x => x + x),
  .scan((acc, x) => acc + x, 0)
  .subscribe(x => console.log(x));复制代码

lettable operator 构成的 pipeline 要比 dot chaining 更贴近于现在流行的 FP 风格。更重要的是,这种方法在引入操作符时比 dot chaining 的 rxjs/add/* 风格方便实用得多。

因此,也有相关的 issue 在讨论是否将 rxjs/add/* 风格的引入方式在 6.0 中发布到另外的 NPM package 中。

lettable operator 的原理

Observable.prototype.pipe 的代码如下:

import { pipeFromArray } from './util/pipe';

class Observable<T> implements Subscribable<T> {
  pipe<R>(...operations: OperatorFunction<T, R>[]): Observable<R> {
    if (operations.length === 0) {
      return this as any;
    }

    return pipeFromArray(operations)(this);
  }
}复制代码

在此调用了 ./util/pipe 中由 pipeFromArray 这个高阶函数所返回的函数:

export function pipeFromArray<T, R>(fns: Array<UnaryFunction<T, R>>): UnaryFunction<T, R> {
  if (!fns) {
    return noop as UnaryFunction<any, any>;
  }

  if (fns.length === 1) {
    return fns[0];
  }

  return function piped(input: T): R {
    return fns.reduce((prev: any, fn: UnaryFunction<T, R>) => fn(prev), input);
  };
}复制代码

在这一过程中,piped 拿到了 Observable 的 this,并以此作为之后 reduce 过程中的初始值。

在这个 reduce 方法结束后,就得到了需要的 Observable。

Reference

文章分类
前端