Promise这个概念在JS开发者里面可谓是深入人心,主要用它来避免callback hell。
1. Promise顾名思义,提供的是一个允诺,这个允诺就是在调用then之后,它会在未来某个阶段把异步函数执行的结果传给then 里的函数
Rx不是允诺,它本质上还是由订阅发布模式印出来的,核心思想就是数据响应式。
(源头)数据产生者——一系列的变换/过滤/合并(操作)——数据消费者使用,数据消费者何时使用,完全取决于数据流何时能流下来
2.Promise需要调用then或catch才能够执行,catch是then的另一种形式,调用then或者catch之后返回一个新的Promise,新的 Promise又可以被调用,因此可以做成无限的then链。
Rx数据是否流出不取决于subscribe,一个observable在未被订阅的时候也可以流出数据,在之后被订阅后先前流出的数据无法被消费者查知的,所以Rx引入了一个lazy模式,允许数据缓存着知道被订阅,但数据是否流出并不依赖subscribe。
observable被订阅后并不是返回新的observable,而是返回一个subsciber,这样可以取消订阅,但是也导致了链式断裂,所以不能像Promise一样组成无线then链。
3.Promise数据是一次性流出的,因为Promise内部维持着状态,初始化的pending,转成resolved或者rejected之后,状态就不可逆转了。
举例说promise().then(A).then(B).then(C).catch(D),数据是顺着链以此传播,但是只有一次,数据从A到B之后,A这个promise的状态发生了改变,从pedding转成了resolved,那么它就不可能再产生内容了,所以这个promise已经不是活动性的了。
而Rx则不同,我们从Rx的接口就可以知道,它有onNext,onComplete和onError,onNext可以响应无数次,这也是符合我们对数据响应式的理解,数据在源头被隔三差五的发出,只要源头认为没有流尽(onComplete)或者出了问题(onError),那么数据就可以不断的流到响应者那边。
举例来说,我们响应一个按钮的点击事件,那么我们可以把这个事件抽象为一个数据流,只要按钮还在,那么我们就认为它是可以产生数据的。
Rx.Observable.fromEvent(btn, 'click')
.map(() => input.value)
.filter(text => !!text)
.distinctUntilChanged()
.flatMapLatest(Rx.Observable.fromPromise(fetch("/").then(res=>res.text()))
.subscribe(value=>{
input.value = value;
});
再比如说定时器,我们可以把一个每三秒执行一项复杂的操作抽象成源头是定时器的操作,比如这样
Rx.Observable.timer(0,3000)
.timeInterval()
.flatMapLatest()
.subscrbe(value=>{
})
4. Promise用then链来处理数据,包括对数据进行过滤、合并、变换等操作,它没有真正意义上的数据消费者,then链的每一个都是数据消费者,所以它非常适合组装流程式,也就是说A完成之后做B,然后B完成后去完成C这种流程,这些流程可以无穷无尽,没有底的。
Rx有数据产生的源头和严格意义的数据消费者,数据可以在中间的操作符里被处理,比如说做过滤,做合并,做节流,变换成新的数据源头等等,可以把它想象成一个完整的数据链,有头也有尾,到了最终消费者那边这个数据流就算到底。
5. Promise的状态发生改变后,我们如果再想重新从源头开始的话,就需要在后续then链递归最开始的那一步,因为后续者是新的promise,无法感知源头的那个Promise。
而Rx的observable可以感知源头,它有类似于retry、repeat这种重新开始的运算符,我们可以很方便的链式调用它,而不需要封装成函数再递归。
6. Promise的then链里面,每一行都是同样的角色,也就是Promise,所以它既可以是源头,也可以是数据处理者。
Rx这边的observable还有一些变种,比如说常用的subject,它可以充当双面角色,可以订阅也可以发消息,这样的话我们还可以用它来做很多封装的工作。
所以Promise和Rx这两个模式的思想差别很清晰,一个是流程式,一个是数据响应式,Promise可以用来贯串一连串单一的流程,而且这个流程是可以无限的,而Rx是用一个数据流来贯串所有操作符,它有一个真正意义上的数据消费者。
我们在哪些场景下用Rx比较方便?首先是需要源源不断的流出数据的场景,因为Promise是一次性的,不适合做这类工作。
比如说把事件/定时器抽象成Rx的Observable更合适,事件可以响应很多次,定时器也可以响应很多次,我们还可以利用Rx的debounce运算符来进行节流,在频繁触发事件的时候过滤那些重复的。
其次是可能需要重试的场景,由于Rx有retry或者repeat这种从源头开始的运算符,我们可以用它来执行比如“出错后重试三次”之类动作,而Promise就需要你递归处理了,破坏了then的链式。
而Promise也有一些优于Rx的场景,比如最开始我们举例的那个结合generate function做的yield自动调用,Promise的链式是无穷的,所以适合这类流程式的工作,async关键字依赖Promise也是正确之举。
还有就是那些一次性的工作,比如说我们请求http api,在得到数据之后response socket就会关闭,这个时候不会有第二次的数据流动,这就是一次性的数据流动,Promise就可以完成的很好。
这两种模式都有自己的想法,所以在使用Rx的时候,不要把它当成Promise来用,记住它的本质是数据响应。