Angular9 与其他技术比较

2020-07-02 15:43 更新

可观察对象与其它技术的比较

你可以经常使用可观察对象(Observable)而不是承诺(Promise)来异步传递值。 类似的,可观察对象也可以取代事件处理器的位置。最后,由于可观察对象传递多个值,所以你可以在任何可能构建和操作数组的地方使用可观察对象。

在这些情况下,可观察对象的行为与其替代技术有一些差异,不过也提供了一些显著的优势。下面是对这些差异的详细比较。

可观察对象 vs. 承诺

可观察对象经常拿来和承诺进行对比。有一些关键的不同点:

  • 可观察对象是声明式的,在被订阅之前,它不会开始执行。承诺是在创建时就立即执行的。这让可观察对象可用于定义那些应该按需执行的菜谱。

  • 可观察对象能提供多个值。承诺只提供一个。这让可观察对象可用于随着时间的推移获取多个值。

  • 可观察对象会区分串联处理和订阅语句。承诺只有 .then() 语句。这让可观察对象可用于创建供系统的其它部分使用而不希望立即执行的复杂菜谱。

  • 可观察对象的 subscribe() 会负责处理错误。承诺会把错误推送给它的子承诺。这让可观察对象可用于进行集中式、可预测的错误处理。

创建与订阅

  • 在有消费者订阅之前,可观察对象不会执行。subscribe() 会执行一次定义好的行为,并且可以再次调用它。每次订阅都是单独计算的。重新订阅会导致重新计算这些值。

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

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


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

  • 承诺会立即执行,并且只执行一次。当承诺创建时,会立即计算出结果。没有办法重新做一次。所有的 then 语句(订阅)都会共享同一次计算。

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

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


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

串联

  • 可观察对象会区分各种转换函数,比如映射和订阅。只有订阅才会激活订阅者函数,以开始计算那些值。

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

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

  • 承诺并不区分最后的 .then() 语句(等价于订阅)和中间的 .then() 语句(等价于映射)。

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

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

可取消

  • 可观察对象的订阅是可取消的。取消订阅会移除监听器,使其不再接受将来的值,并通知订阅者函数取消正在进行的工作。

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');
    });

速查表

下列代码片段揭示了同样的操作要如何分别使用可观察对象和承诺进行实现。

操作 可观察对象 承诺
创建 new Observable((observer) => { observer.next(123); }); new Promise((resolve, reject) => { resolve(123); }); 
转换 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 中的事件处理器很像。这两种技术都会定义通知处理器,并使用它们来处理一段时间内传递的多个值。订阅可观察对象与添加事件处理器是等价的。一个显著的不同是你可以配置可观察对象,使其在把事件传给事件处理器之前先进行转换。

使用可观察对象来处理错误和异步操作在 HTTP 请求这样的场景下更加具有一致性。

下列代码片段揭示了同样的操作要如何分别使用可观察对象和事件 API 进行实现。

  1. “创建与取消”操作。

  • 可观察对象。

    // 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. 配置操作。

  • 可观察对象。

监听按键,提供一个流来表示这些输入的值。

    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. 数组

可观察对象会随时间生成值。数组是用一组静态的值创建的。某种意义上,可观察对象是异步的,而数组是同步的。 在下面的例子中, 符号表示异步传递值。

  1. 给出值。

  • 可观察对象。

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

  • 数组。

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

  1. concat()

  • 可观察对象。

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

  • 数组。

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

  1. filter()

  • 可观察对象。

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

  • 数组。

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

  1. find()

  • 可观察对象。

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

  • 数组。

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

  1. findIndex()

  • 可观察对象。

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

  • 数组。

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

  1. forEach()

  • 可观察对象。

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

  • 数组。

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

  1. map()

  • 可观察对象。

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

  • 数组。

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

  1. reduce()

  • 可观察对象。

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

  • 数组。

    arr.reduce((s,v) => s+v, 0)
    18
以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号