Angular9 與其他技術(shù)比較

2020-07-02 15:43 更新

可觀察對象與其它技術(shù)的比較

你可以經(jīng)常使用可觀察對象(Observable)而不是承諾(Promise)來異步傳遞值。 類似的,可觀察對象也可以取代事件處理器的位置。最后,由于可觀察對象傳遞多個值,所以你可以在任何可能構(gòu)建和操作數(shù)組的地方使用可觀察對象。

在這些情況下,可觀察對象的行為與其替代技術(shù)有一些差異,不過也提供了一些顯著的優(yōu)勢。下面是對這些差異的詳細比較。

可觀察對象 vs. 承諾

可觀察對象經(jīng)常拿來和承諾進行對比。有一些關(guān)鍵的不同點:

  • 可觀察對象是聲明式的,在被訂閱之前,它不會開始執(zhí)行。承諾是在創(chuàng)建時就立即執(zhí)行的。這讓可觀察對象可用于定義那些應(yīng)該按需執(zhí)行的菜譜。

  • 可觀察對象能提供多個值。承諾只提供一個。這讓可觀察對象可用于隨著時間的推移獲取多個值。

  • 可觀察對象會區(qū)分串聯(lián)處理和訂閱語句。承諾只有 .then() 語句。這讓可觀察對象可用于創(chuàng)建供系統(tǒng)的其它部分使用而不希望立即執(zhí)行的復(fù)雜菜譜。

  • 可觀察對象的 subscribe() 會負責(zé)處理錯誤。承諾會把錯誤推送給它的子承諾。這讓可觀察對象可用于進行集中式、可預(yù)測的錯誤處理。

創(chuàng)建與訂閱

  • 在有消費者訂閱之前,可觀察對象不會執(zhí)行。subscribe() 會執(zhí)行一次定義好的行為,并且可以再次調(diào)用它。每次訂閱都是單獨計算的。重新訂閱會導(dǎo)致重新計算這些值。

Path:"src/observables.ts (observable)" 。

    // declare a publishing operation
    const observable = new Observable<number>(observer => {
      // Subscriber fn...
    });


    // initiate execution
    observable.subscribe(() => {
      // observer handles notifications
    });

  • 承諾會立即執(zhí)行,并且只執(zhí)行一次。當(dāng)承諾創(chuàng)建時,會立即計算出結(jié)果。沒有辦法重新做一次。所有的 then 語句(訂閱)都會共享同一次計算。

Path:"src/promises.ts (promise)" 。

    // initiate execution
    const promise = new Promise<number>((resolve, reject) => {
      // Executer fn...
    });


    promise.then(value => {
      // handle result here
    });

串聯(lián)

  • 可觀察對象會區(qū)分各種轉(zhuǎn)換函數(shù),比如映射和訂閱。只有訂閱才會激活訂閱者函數(shù),以開始計算那些值。

Path:"src/observables.ts (chain)" 。

    observable.pipe(map(v => 2 * v));

  • 承諾并不區(qū)分最后的 .then() 語句(等價于訂閱)和中間的 .then() 語句(等價于映射)。

Path:"src/promises.ts (chain)" 。

    promise.then(v => 2 * v);

可取消

  • 可觀察對象的訂閱是可取消的。取消訂閱會移除監(jiān)聽器,使其不再接受將來的值,并通知訂閱者函數(shù)取消正在進行的工作。

Path:"src/observables.ts (unsubcribe)" 。

    const subscription = observable.subscribe(() => {
      // observer handles notifications
    });


    subscription.unsubscribe();

  • 承諾是不可取消的。

錯誤處理

  • 可觀察對象的錯誤處理工作交給了訂閱者的錯誤處理器,并且該訂閱者會自動取消對這個可觀察對象的訂閱。

Path:"src/observables.ts (error)" 。

    observable.subscribe(() => {
      throw Error('my error');
    });

  • 承諾會把錯誤推給其子承諾。

Path:"src/promises.ts (error)" 。

    promise.then(() => {
      throw Error('my error');
    });

速查表

下列代碼片段揭示了同樣的操作要如何分別使用可觀察對象和承諾進行實現(xiàn)。

操作 可觀察對象 承諾
創(chuàng)建 new Observable((observer) => { observer.next(123); }); new Promise((resolve, reject) => { resolve(123); }); 
轉(zhuǎn)換 obs.pipe(map((value) => value * 2)); promise.then((value) => value * 2); 
訂閱 sub = obs.subscribe((value) => { console.log(value) }); promise.then((value) => { console.log(value); }) 
取消訂閱 sub.unsubscribe(); 承諾被解析時隱式完成。

可觀察對象 vs. 事件 API

可觀察對象和事件 API 中的事件處理器很像。這兩種技術(shù)都會定義通知處理器,并使用它們來處理一段時間內(nèi)傳遞的多個值。訂閱可觀察對象與添加事件處理器是等價的。一個顯著的不同是你可以配置可觀察對象,使其在把事件傳給事件處理器之前先進行轉(zhuǎn)換。

使用可觀察對象來處理錯誤和異步操作在 HTTP 請求這樣的場景下更加具有一致性。

下列代碼片段揭示了同樣的操作要如何分別使用可觀察對象和事件 API 進行實現(xiàn)。

  1. “創(chuàng)建與取消”操作。

  • 可觀察對象。

    // Setup
    let clicks$ = fromEvent(buttonEl, ‘click’);
    // Begin listening
    let subscription = clicks$
      .subscribe(e => console.log(‘Clicked’, e))
    // Stop listening
    subscription.unsubscribe();

  • 事件 API。

    function handler(e) {
      console.log(‘Clicked’, e);
    }
    // Setup & begin listening
    button.addEventListener(‘click’, handler);
    // Stop listening
    button.removeEventListener(‘click’, handler);

  1. 配置操作。

  • 可觀察對象。

監(jiān)聽按鍵,提供一個流來表示這些輸入的值。

    fromEvent(inputEl, 'keydown').pipe(
      map(e => e.target.value)
    );

  • 事件 API。

不支持配置。

    element.addEventListener(eventName, (event) => {
      // Cannot change the passed Event into another
      // value before it gets to the handler
    });

  1. 訂閱操作。

  • 可觀察對象。

    observable.subscribe(() => {
      // notification handlers here
    });

  • 事件 API。

    element.addEventListener(eventName, (event) => {
      // notification handler here
    });

可觀察對象 vs. 數(shù)組

可觀察對象會隨時間生成值。數(shù)組是用一組靜態(tài)的值創(chuàng)建的。某種意義上,可觀察對象是異步的,而數(shù)組是同步的。 在下面的例子中,? 符號表示異步傳遞值。

  1. 給出值。

  • 可觀察對象。

    obs: ?1?2?3?5?7
    obsB: ?'a'?'b'?'c'

  • 數(shù)組。

    arr: [1, 2, 3, 5, 7]
    arrB: ['a', 'b', 'c']

  1. concat()

  • 可觀察對象。

    concat(obs, obsB)
    ?1?2?3?5?7?'a'?'b'?'c'

  • 數(shù)組。

    arr.concat(arrB)
    [1,2,3,5,7,'a','b','c']

  1. filter()

  • 可觀察對象。

    obs.pipe(filter((v) => v>3))
    ?5?7

  • 數(shù)組。

    arr.filter((v) => v>3)
    [5, 7]

  1. find()。

  • 可觀察對象。

    obs.pipe(find((v) => v>3))
    ?5

  • 數(shù)組。

    arr.find((v) => v>3)
    5

  1. findIndex()

  • 可觀察對象。

    obs.pipe(findIndex((v) => v>3))
    ?3

  • 數(shù)組。

    arr.findIndex((v) => v>3)
    3

  1. forEach()。

  • 可觀察對象。

    obs.pipe(tap((v) => {
      console.log(v);
    }))
    1
    2
    3
    5
    7

  • 數(shù)組。

    arr.forEach((v) => {
      console.log(v);
    })
    1
    2
    3
    5
    7

  1. map()。

  • 可觀察對象。

    obs.pipe(map((v) => -v))
    ?-1?-2?-3?-5?-7

  • 數(shù)組。

    arr.map((v) => -v)
    [-1, -2, -3, -5, -7]

  1. reduce()

  • 可觀察對象。

    obs.pipe(reduce((s,v)=> s+v, 0))
    ?18

  • 數(shù)組。

    arr.reduce((s,v) => s+v, 0)
    18
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號