Angular 記錄 - Observable 概述

路馬發表於2019-12-25

Angular 記錄 - Observable 概述

Angular 簡介

Angular 由 google 建立,於 2010 年正式釋出,到目前已經更新到 v8.0 版本。相比於目前炙手可熱的 React 與 Vue 框架來說,Angular 自帶完備的生態系統使其更能被稱之為一款 “ 框架 ”.

Angular 對 typescript 的完美支援(angular 本身就是由 typescript 去編寫的)以及它對模組化和層次依賴注入的架構方式,使其在構建微服務前端系統也有一定優勢. 此外 Angular 框架中引入 Rxjs(ReactiveX javascript) , 運用 Observable 去解決非同步問題,使非同步問題變的更加簡單和可控。


Angular 記錄 - 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 。

感謝您的閱讀~

Angular 記錄 - Observable 概述

相關文章