rxjs 操作符(operator)和operation的原理

1,053 阅读2分钟

目录:

  • 一.what is operator ?
  • 二.how this happend?

正文:

what is operator ?

先来看一个栗子:

import { of } from 'rxjs';
import { tap, pluck, mapTo } from 'rxjs/operators';

of({val: 1}).pipe(
    tap(i => console.log(i)),
    pluck('val'),
    mapTo('hello world')
)

pipe中的一个个函数被称作operator,它定义了数据的处理步骤,并可以将处理后的值传递下去

how this happend ?

每个operator都会返回一个operationoperation其实是一个函数,拿tap举个栗子,在rxjs/src/internal/operator/tap.ts:

export function tap<T>(nextOrObserver?: PartialObserver<T> | ((x: T) => void),
                       error?: (e: any) => void,
                       complete?: () => void): MonoTypeOperatorFunction<T> {
  return function tapOperatorFunction(source: Observable<T>): Observable<T> {
    return source.lift(new DoOperator(nextOrObserver, error, complete));
  };
}

class DoOperator<T> implements Operator<T, T> {
  constructor(private nextOrObserver?: PartialObserver<T> | ((x: T) => void),
              private error?: (e: any) => void,
              private complete?: () => void) {
  }
  call(subscriber: Subscriber<T>, source: any): TeardownLogic {
    return source.subscribe(new TapSubscriber(subscriber, this.nextOrObserver, this.error, this.complete));
  }
}

从上面的代码可以看出:

  • tapOperatorFunction就是operation, 它的入参和回参都是Observable

  • source是上一个Observable,这里指的是of({val: 1}),它的lift方法将创建一个包裹着tapOperatorFunctionObservablelift()定义在rxjs/src/internal/Observable.ts:

    lift<R>(operator: Operator<T, R>): Observable<R> {
        const observable = new Observable<R>();
        observable.source = this;
        observable.operator = operator;
        return observable;
    }
    

    它接受一个operator,返回一个Observable,这里的this运用了隐式绑定,指向了of({val: 1}) 还记得在rxjs-pipeline中的一个庞然大物吗:

    result of pipe
    现在你应该知道lift方法其实创建一个新的包裹着当前操作符的Observale,并且通过source引用上一个Observale,从而形成了父子关系,并在最后返回了子节点,子节点又会被当成入参传入下一个操作符的operation中,这个流程被定义在rxjs/src/internal/util/pipe.ts

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

    然后tap的儿子又是pluck,并一直往下构建,最终形成了这个庞然大物

  • lift方法所接收的Operator将由各自的操作符定义,这意味tap对应DoOperator类,而pluckmapTo又有着不一样的,但它们都会实现Operator接口的call方法,它定义在rxjs/src/internal/Operator.ts

    export interface Operator<T, R> {
        call(subscriber: Subscriber<R>, source: any): TeardownLogic;
    }
    
  • call方法将会在最终的subscribe阶段被调用,这是我们传入操作符的函数或者参数就会被调用,操作符也会发挥它真正的功能,具体过程请看rxjs subscribe原理