Angular 记录 - Observable 概述

2,622 阅读4分钟

Angular 简介

Angular 由 google 创建,于 2010 年正式发布,到目前已经更新到 v8.0 版本。相比于目前炙手可热的 React 与 Vue 框架来说,Angular 自带完备的生态系统使其更能被称之为一款 “ 框架 ”.

Angular 对 typescript 的完美支持(angular 本身就是由 typescript 去编写的)以及它对模块化和层次依赖注入的架构方式,使其在构建微服务前端系统也有一定优势. 此外 Angular 框架中引入 Rxjs(ReactiveX javascript) , 运用 Observable 去解决异步问题,使异步问题变的更加简单和可控。


Observable (可观察对象)

社区对于 Observables, Observable, Observer, Subject, Subscription 等有很多介绍,概念性的知识点很难记忆,我概述了几条,并记录顺便分享一下自己对这些概念的理解:

(1) Observables是Rx的核心, Rx 是(Reactive Extensions)的简写,而 rxjs 则是 Observable 在 Javascript 版本的实现,除了 Rxjs, 还有 RxJava, RxDart, RxSwift 等语言的实现版本。

(2) Rxjs 依据 Observable 作为核心,对可观察对象 Observable 进行赋能,将其转换为操作流,通过 operators 操作符完成对数据流的变换、组合、操纵等。

(3) Observable 是一种新的 push 体系(Pull 和 Push 是两种不同的协议,用于描数据的生产者 和 数据消费者之间的联系方式)。可以通过以下例子简单加以理解:

   function GetName()
   {
       const name: string = 'i come from a method';
       return name;
   }
   
   const consumerName: string = GetName(); // consumerName 'i come from a method'

JS 通过调用函数或取数据,此时函数是数据的产生者。函数必须被 call 即调用才可以被触发,这就是一种 pull(拉取) 体系。

那么在 Rxjs 中:

   import { Observable } from 'rxjs';
   const NameObservables$: Observable<string> = new Observable(
       subscriber => {
           const name: string = 'i come from a method';
           subscriber.next(name);
       }
   );
   
   NameObservables$.subscribe((name: string) => {
       console.log(name); // 'i come from a method'
   });
   

代码中,我们通过 subscribe 来获取数据,这和直接调用函数有什么区别呢?

观察代码我们可以发现,我们将函数和消费者之前进行了解耦,我们只是订阅了数据生产者返回的NameObservables$这个可观察对象,这使得我们在获取原数据的操作上多了一层隔离,利用这层隔离,我们可以做很多事,比如利用各种operators对原数据进行各类操作, 并且我们对一类数据源进行了一定程度的复用,因为任何需要 name 的地方都可以订阅这个 Observable。

Observable 区别于普通函数的另一个特点就是,Observable 可以异步返回多个值,并且由于上面提到的隔离,我们可以针对不同的场景分别去处理这些值,只要订阅 Observable 对象的订阅者们,都可以获取到这些数据。大概像这样:

   import { Observable } from 'rxjs';
   const NameObservables$: Observable<string> = new Observable(
       subscriber => {
           const name: string = 'i come from a method';
           subscriber.next(name);
           const info: any = 'i am happy';
           subscriber.next(info);
           setTimeout(() => {
               const info2: any = 'i like rxjs';
               subscriber.next(info2);
           }, 2000)
       }
   );
   
   NameObservables$.subscribe((name: string) => {
       console.log(name); // 'i come from a method'
       console.log(name); // 'i am happy'
       console.log(name); // delay 2s => 'i like rxjs'
   });
   

(4) 可观察对象 Observable 本质也是基于 订阅者模式 的一种应用,一个 Observable 构造函数可以创建任意类型的观察流供观察者(Observer)进行订阅操作。(订阅?:也就是获取数据,可以简单理解为订阅微信公众号)。生活中我们通过关注一个公众号获取订阅通知,在 Observable 的世界里,则通过 订阅(subscribe)操作去获取可观察对象流转出来的信息。这个过程中代码简单模拟一下如下:

  class Observable {
          constructor(subscribe){
              this.subscriptions = [];
              this._subscribe = subscribe;
          }
          subscribe(observer){
              const _this = this;
              
              this.subscriptions.push(observer);
              this._subscribe.call(this, observer);
              return {
                  unsubscribe: () => {
                      const subscriptionIndex = _this.subscriptions.indexOf(observer);
                      _this.subscriptions.splice(subscriptionIndex, 1);
                  }
              }
      }
  }
  
  let testObservable$ = new Observable(function(observer){
      observer.next(1);
      observer.next(2);
      observer.next(3);
      observer.complete();
  });
  
  const subscription = testObservable$.subscribe({
      next:function(val){ console.log('next', val); },
      error:function(error){ console.log('error', error); },
      complete:function(){ console.log('complete'); }
  });
  // 取消订阅,不再接受消息
  setTimeout(() => {
      subscription.unsubscribe();
  }, 2000);

(5) 通过上面的代码也可以发现,一个Observer(观察者) 能获取到 3 种通知,即 next, error, complete. 当有人订阅Observable的实例时,需要通过调用 subscribe 方法来获取到一个 observer对象,subscribe 方法会返回一个 subscription 对象,调用 subscirption 的 unsubscribe 方法, 订阅者 Observer 就会停止接收通知。原有的Observable对象依然存在。

(6) 结合上面了解到的 Observable 特性,我们可以对比 Promise体系。熟悉 Promise 的朋友都知道 ,Promise 的 executor 函数接受 resolve, reject 方法,通过调用 resolve 或者 reject 来触发回调数组,将 resolve 的 data 或者是 reject 的 data 通过回调,返回给 then 方法中的 onFulfilled 函数。Promise 单个体系中,因为这些 value 被回调回去的时间是非常确定的,所以 promise 也是一种 push 体系, promise 一旦被 resolve 之后便无法再次返回和取消了,至少他本身不具备取消的能力。下面是 promise 部分简单的实现:


class Promise {
    constructor(executor) {
        this.status = PENDING;
        this.value = void 0;
        this.err = void 0;
        
        this.fulfilledList = new Array();  // fulfilled sub lists
        this.rejectedList = new Array();   // rejected sub lists

        let resolve = (value) => {
            if (this.status === PENDING) {
                ...
                this.fulfilledList.forEach(cb => cb());
            }
        }

        let reject = (err) => {
            if (this.status === PENDING) {
                ...
                this.rejectedList.forEach(cb => cb());
            }
        }
        
        try {
            executor(resolve, reject);
        } catch (e) {
            reject(e);
        }
    }

    then(onFulfilled, onRejected) {
        let promiseThen = void 0;
        promiseThen = new Promise((resolveNext, rejectNext) => {
            ....
            if (this.status === PENDING) {
                this.fulfilledList.push(() => {
                    ...
                    let result = onFulfilled(this.value);
                    ...
                });
                this.rejectedList.push(() => {
                    ...
                    let result = onRejected(this.err);
                    ...
                });
            }
        });
        return promiseThen;
    }
}

了解 Observable 的诸多概念之后,我总结了以下几点:

(1) Observable 是一种新的 push 体系, 它通过把异步操作封装成一个可以自由组合,灵活变化的可观察对象,来对异步数据流进行控制,是一种高级的 promise。

(2) Observable 是 Rxjs 的核心,真正强大的是 Observable 这种观察和订阅模式配合 Rxjs 各类操作符实现对异步流的灵活控制 。

(3) Observable 对比 Promise 拥有可取消,多复用,多值返回, 异步可控等优点。

(4) Observable 数据源可以保留,并可以被多个观察者使用的特性,使得跨组件的通讯变更加简单。

了解了 Observable 的概念及简单原理,我们可以更好的在实际业务场景去使用 Observable 和 Rxjs 。

感谢您的阅读~