异步数据流的响应式编程库Rxjs(十二)- Rxjs原理之Observable

84 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情

工作中使用Angular开发项目,对Rxjs有一定的熟悉和理解,但是感觉从理解上还觉得有些地方理解的不够深入和透彻。首先需要整体上去学一下Rxjs,了解一下前因后果,对Rxjs的进阶还是很有帮助的。想进阶精通一个库的最好办法就是学习它的源码,学了源码后,API使用起来才能更加得心应手。

我从整体上过了一下Rxjs的主要代码,而且很多细节我都没有去考虑,也就是先范读,从整体上把它串一下,下一步可以再去精读。本文就是第一步,先范读,而且基于范读会手写一个简版的Rxjs,虽然简陋,但是可以实现Rxjs的基本功能的。源码学习是基于Rxjs 7.5.5

我们先从几个核心概念来一点点学习。

Observable

我们先来看一下通过new创建的Observable

const { Observable } = require('rxjs');

const source$ = new Observable((observer) => {
    observer.next(1);
    observer.next(2);
    observer.complete();
});

let subscription1 = source$.subscribe({
    next(data){
        console.log(data);
    },
    error(error){
        console.log('error');
    },
    complete(){
        console.log('complete');
    }
});

console.log('End');

输出:

1
2
complete
End

首先通过new Observable创建了一个可观察对象,然后通过subscribe方法订阅了一下,订阅后就会执行new Observable时传入的函数参数。

总结一下:

  1. Observable是一个构造函数,构造函数里有一个函数作为参数,这个函数参数里有一个参数observer对象,这个对象有nexterrorcomplete等方法;
  2. 可观察对象Observable有一个subscribe方法,subscribe方法有一个参数,这个参数可以是一个object对象,也可以是一个函数;

有了上面的两点总结,一会就可以先把代码框架搭建起来了。继续之前,先看下源码里的Observable类。

export class Observable<T> implements Subscribable<T> {
  /**
  * @constructor
  * @param {Function} subscribe the function that is called when the Observable is
  * initially subscribed to. This function is given a Subscriber, to which new values
  * can be `next`ed, or an `error` method can be called to raise an error, or
  * `complete` can be called to notify of a successful completion.
  */
  constructor(subscribe?: (this: Observable<T>, subscriber: Subscriber<T>) => TeardownLogic) {
    if (subscribe) {
      this._subscribe = subscribe;
    }
  }
  ...
}

在来看一下订阅函数subscribesubscribe方法签名有多个,只是传参形式不同。

export class Observable<T> implements Subscribable<T> {
  ...
  subscribe(observer?: Partial<Observer<T>>): Subscription;
  subscribe(next: (value: T) => void): Subscription;
  subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription;
}

对于参数observer,它和Observer接口相关,这个接口有三个属性:

export interface Observer<T> {
  next: (value: T) => void;
  error: (err: any) => void;
  complete: () => void;
}

subscribe方法的参数,可以是一个对象,这个对象有三个属性,next,error,complete,也可以是一个函数,也就是next

subscribe(
    observerOrNext?: Partial<Observer<T>> | ((value: T) => void) | null,
    error?: ((error: any) => void) | null,
    complete?: (() => void) | null
  ): Subscription {
    const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);

    errorContext(() => {
      const { operator, source } = this;
      subscriber.add(
        operator
          ? // We're dealing with a subscription in the
            // operator chain to one of our lifted operators.
            operator.call(subscriber, source)
          : source
          ? // If `source` has a value, but `operator` does not, something that
            // had intimate knowledge of our API, like our `Subject`, must have
            // set it. We're going to just call `_subscribe` directly.
            this._subscribe(subscriber)
          : // In all other cases, we're likely wrapping a user-provided initializer
            // function, so we need to catch errors and handle them appropriately.
            this._trySubscribe(subscriber)
      );
    });

    return subscriber;
  }

subscribe的参数,可能是只有next和error的对象,但是我们期望它是有next,error和complete的,所以我们要把它补全,方便后续的操作。因此,就在把这个参数封装一下,也就是new SafeSubscriber(...)

export class SafeSubscriber<T> extends Subscriber<T> {
  constructor(
    observerOrNext?: Partial<Observer<T>> | ((value: T) => void) | null,
    error?: ((e?: any) => void) | null,
    complete?: (() => void) | null
  ) {
    super();

    let partialObserver: Partial<Observer<T>>;
    if (isFunction(observerOrNext) || !observerOrNext) {
      partialObserver = {
        next: observerOrNext ?? undefined,
        error: error ?? undefined,
        complete: complete ?? undefined,
      };
    } else {
       ...
        partialObserver = {
          next: observerOrNext.next && bind(observerOrNext.next, context),
          error: observerOrNext.error && bind(observerOrNext.error, context),
          complete: observerOrNext.complete && bind(observerOrNext.complete, context),
        };
    }
    ...
  }
}

SafeSubscriber的主要作用是对next,error,complete三个属性进行了一层封装。判断了observerOrNext是否为函数,如果是函数则它就是next函数,如果不是函数,那它就是一个object对象。

另外的话,还需要考虑unsubscribe取消订阅,需要维护一个是否closed的状态,等被取消订阅的时候,就把这状态置为true,当取消订阅,error,或者complete的时候,都需要改变一下这个状态。

好了,已经梳理了差不多了,我们只关注了高层级的代码逻辑,至于具体细节实现暂时先忽略。

启动手写部分哦。

class SafeSubscriber {
    constructor(subscriber) {
        this.closed = false;

        this.next = (data) => {
            if (!this.closed) {
                if (typeof subscriber === 'function') {
                    subscriber(data);
                }
                else {
                    subscriber.next(data);
                }
            }
        };

        this.error = (error) => {
            subscriber.error && subscriber.error(error);
            
            this.closed = true;
        };

        this.complete = () => {
            subscriber.complete && subscriber.complete();
            
            this.closed = true;
        };

        this.unsubscribe = () => {
            this.closed = true;
        };
    }
}

class Observable {
    constructor(subscribe){
        this._subscribe = subscribe;
    }

    subscribe(observer) {
        let subscriber = new SafeSubscriber(observer);

        try {
            this._subscribe(subscriber);
        }
        catch(error){
            subscriber.error(error);
        }
        
        return subscriber;
    }
}

可以测试一下,发现没有问题哦。虽然功能还比较初级,但是可以正常工作,离Rxjs又近了一步,后续文章会继续学原理哦。