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

162 阅读2分钟

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

Subject

Subject是一个多播的Observable,每个Subject既是一个Observable可观察对象,又是一个Observer观察者,所以Subjectsubscribe方法,继承自Observable,也有nexterrorcomplete方法,继承自Subscription。先来看一下Subject的源码部分吧。

export class Subject<T> extends Observable<T> implements SubscriptionLike {
  closed = false;

  private currentObservers: Observer<T>[] | null = null;
  observers: Observer<T>[] = [];

  constructor() {
    // NOTE: This must be here to obscure Observable's constructor.
    super();
  }
  
  next(value: T) {
    errorContext(() => {
      this._throwIfClosed();
      if (!this.isStopped) {
        if (!this.currentObservers) {
          this.currentObservers = Array.from(this.observers);
        }
        for (const observer of this.currentObservers) {
          observer.next(value);
        }
      }
    });
  }
  
  error(err: any) {
    errorContext(() => {
      this._throwIfClosed();
      if (!this.isStopped) {
        this.hasError = this.isStopped = true;
        this.thrownError = err;
        const { observers } = this;
        while (observers.length) {
          observers.shift()!.error(err);
        }
      }
    });
  }
  
  complete() {
    errorContext(() => {
      this._throwIfClosed();
      if (!this.isStopped) {
        this.isStopped = true;
        const { observers } = this;
        while (observers.length) {
          observers.shift()!.complete();
        }
      }
    });
  }
  
  unsubscribe() {
    this.isStopped = this.closed = true;
    this.observers = this.currentObservers = null!;
  }
  
  asObservable(): Observable<T> {
    const observable: any = new Observable<T>();
    observable.source = this;
    return observable;
  }
}

Subject有一个共有属性observers,存储所有的observer观察者。还有next,error,complete,unsubscribe,asObservable等,这些都是我们要实现的。这些的基本功能这里就不再回顾了,不熟悉的小伙伴可以看我之前的几篇文章哦。

开始写代码。这里的实现和源码是有些不同的,源码里的Subject是要继承Observable的,这里简化了这个实现,所以asObservable的实现也不一样。一切从简,先实现一个基础版的Subject,但是这些基本功能都是会有的,后续可能会继续在实现一版更接近源码的实现。

class Subject {
    closed = false;
    observers = [];
    
    constructor(){
        
    }

    next(data){
        if (!this.closed) {
            this.observers.forEach((subscriber) => {
                subscriber.next(data);
            });
        }
    }

    error(error){
        if (!this.closed) {
            this.observers.forEach((subscriber) => {
                subscriber.error(error);
            });
        }

        this.closed = true;
        this.observers = [];
    }

    complete(){
        if (!this.closed) {
            this.observers.forEach((subscriber) => {
                subscriber.complete();
            });
        }

        this.closed = true;
        this.observers = [];
    }

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

        this.observers.push(subscriber);
        
        return subscriber;
    }

    unsubscribe(){
        this.closed = true;
        this.observers = [];
    }

    asObservable(){
        return new Observable((observer) => {
            this.subscribe({
                next(data){
                    observer.next(data);
                },
                error(error){
                    observer.error(error);
                },
                complete(){
                    observer.complete();
                }
            });
        });
    }
}

BehaviorSubject

实现了Subject,再来实现BehaviorSubject就容易的多了,BehaviorSubject一旦有订阅会发出上一次的值。开始撸代码哦。

class BehaviorSubject {
    closed = false;
    observers = [];

    constructor(value){
        this._value  = value;
    }

    next(value){
        if (!this.closed) {
            this.observers.forEach((subscriber) => {
                subscriber.next(value);
            });

            this._value  = value;
        }
    }

    error(error){
        if (!this.closed) {
            this.observers.forEach((subscriber) => {
                subscriber.error(error);
            });
        }

        this.closed = true;
        this.observers = [];
    }

    complete(){
        if (!this.closed) {
            this.observers.forEach((subscriber) => {
                subscriber.complete();
            });
        }

        this.closed = true;
        this.observers = [];
    }

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

        this.observers.push(subscriber);
        subscriber.next(this._value);
        
        return subscriber;
    }

    unsubscribe(){
        this.closed = true;
        this.observers = [];
    }

    asObservable(){
        return new Observable((observer) => {
            this.subscribe({
                next(data){
                    observer.next(data);
                },
                error(error){
                    observer.error(error);
                },
                complete(){
                    observer.complete();
                }
            });
        });
    }
}

总结

我们实现了ObservableasyncScheduler,部分操作符,还有SubjectBehaviorSubject,尽管还比较简陋,但是大体功能是可以工作的。最后把自己的Rxjs库导出就可以使用了。

module.exports = {
  Observable,
  map,
  of,
  asyncScheduler,
  interval,
  Subject,
  BehaviorSubject
};

测试一下代码,看看有没有什么问题。

const { Subject, BehaviorSubject, Observable, map, of, asyncScheduler, interval } = require("./rxjs.js");

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

let subscription1 = source$.pipe(map((data) => data * 2), map((data) => data + 1)).subscribe({
    next(data){
        console.log(data);
    },
    error(error){
        console.log('error', error);
    },
    complete(){
        console.log('complete');
    }
});

let subscription2 = source$.subscribe({
    next(data){
        console.log(data);
    }
});

let s2$ = of(2,7,9);
s2$.subscribe((data) => {
    console.log(data);
});

asyncScheduler.schedule(() => {
    console.log('我是异步调度器');
}, 2000);

let s3$ = interval(1000);
let subscription3 = s3$.subscribe((data) => {
    console.log(data);
});

setTimeout(() => {
    subscription3.unsubscribe();
},5000);

let s1$ = new Subject();
let sub1 = s1$.subscribe((data) => {
    console.log("第一个订阅:", data);
});

s1$.next(1);

s1$.subscribe((data) => {
    console.log("第二个订阅:", data);
});

s1$.next(2);
let s4$ = s1$.asObservable();
s4$.subscribe((data) => {
    console.log("asObservable:", data);
});

s4$.next(3);

let bs$ = new BehaviorSubject(666);
bs$.next('888');

bs$.subscribe((data) => {
    console.log(1, data);
});

bs$.subscribe((data) => {
    console.log(2, data);
});

bs$.next('999');

bs$.asObservable().subscribe((data) => {
    console.log(3, data);
});

console.log('End');

居然可以完美运行,哈哈哈。 Rxjs的源码部分就到此为止了,后续根据情况在做进一步的深化学习哦,但是不要忘了学以致用,多多使用才能体会Rxjs的神奇强大之处。

参考文章

Rxjs源码解析