与其它技术的比较

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

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

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

可观察对象 vs. 承诺

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

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

创建与订阅

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

content_copy// declare a publishing operation new Observable((observer) => { subscriber_fn } // initiate execution observable.subscribe(() => { // observer handles notifications }

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

content_copy// initiate execution new Promise((resolve, reject) => { executer_fn } // handle return value promise.then((value) => { // handle result here }

串联

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

content_copyobservable.map((v) => 2*v

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

content_copypromise.then((v) => 2*v

可取消

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

content_copyconst sub = obs.subscribe(... sub.unsubscribe(

  • 承诺是不可取消的。

错误处理

  • 可观察对象的错误处理是交给订阅者的错误处理器的,并且该订阅者会自动取消对这个可观察对象的订阅。

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

  • 承诺会把错误推给其子承诺。

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

速查表

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

操作可观察对象承诺
创建new Observable((observer) => { observer.next(123 }new Promise((resolve, reject) => { resolve(123 }
转换obs.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 进行实现。

可观察对象事件 API
创建与取消// Setup let clicks$ = fromEvent(buttonEl, ‘click’ // Begin listening let subscription = clicks$ .subscribe(e => console.log(‘Clicked’, e)) // Stop listening subscription.unsubscribe(function handler(e) { console.log(‘Clicked’, e } // Setup & begin listening button.addEventListener(‘click’, handler // Stop listening button.removeEventListener(‘click’, handler
订阅observable.subscribe(() => { // notification handlers here }element.addEventListener(eventName, (event) => { // notification handler here }
配置监听按键,提供一个流来表示这些输入的值。fromEvent(inputEl, 'keydown').pipe( map(e => e.target.value) 不支持配置。element.addEventListener(eventName, (event) => { // Cannot change the passed Event into another // value before it gets to the handler }

可观察对象 vs. 数组

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

可观察对象数组
给出值obs: ➞1➞2➞3➞5➞7obsB: ➞'a'➞'b'➞'c'arr: [1, 2, 3, 5, 7]arrB: ['a', 'b', 'c']
concat()obs.concat(obsB)➞1➞2➞3➞5➞7➞'a'➞'b'➞'c'arr.concat(arrB)[1,2,3,5,7,'a','b','c']
filter()obs.filter((v) => v>3)➞5➞7arr.filter((v) => v>3)[5, 7]
find()obs.find((v) => v>3)➞5arr.find((v) => v>3)5
findIndex()obs.findIndex((v) => v>3)➞3arr.findIndex((v) => v>3)3
forEach()obs.forEach((v) => { console.log(v }) 1 2 3 5 7arr.forEach((v) => { console.log(v }) 1 2 3 5 7
map()obs.map((v) => -v)➞-1➞-2➞-3➞-5➞-7arr.map((v) => -v)[-1, -2, -3, -5, -7]
reduce()obs.scan((s,v)=> s+v, 0)➞1➞3➞6➞11➞18arr.reduce((s,v) => s+v, 0)18