本教程将和大家一起来从0到1完成一个Your Own RxJS,同时教程中还会包含自己在实际项目对于「redux + ng-redux + redux-observable + RxJS」应用的体会。好了,第一讲的内容当然是RxJS的灵魂Observable,我们将在Observable这个分支上进行代码演示,工程路径,下面就让我们开始吧。
其实RxJS归根结底还是基于观察者模式的,在这之上又增加了cold,lazy,multicast等概念,但其根基还是观察者模式,下面即经典的观察者模型:
<img src="https://pic1.zhimg.com/v2-97b2c9d386a043e7d7d736702ed03564_b.png" data-rawwidth="603" data-rawheight="143" class="origin_image zh-lightbox-thumb" width="603" data-original="https://pic1.zhimg.com/v2-97b2c9d386a043e7d7d736702ed03564_r.png">我们把范围限定到browser端,哪些可以被理解为被观察者,或者我直接使用RxJS术语,哪些可以被视为
我们把范围限定到browser端,哪些可以被理解为被观察者,或者我直接使用RxJS术语,哪些可以被视为Observable(可观察对象),答案是everything
is observable(有点太狂了:),收一下~),从用户的各种交互操作,到定时器、ajax请求,甚至是一个字符串,其实这些都可以统称为事件,区别就是事件触发时间点的不同,因此事件与时间有了关系,事件流也就出现了,而Observable就是一个。都可以被当成Observable,Observer(观察者)总会在Observable处注册一个订阅,当事件发生时,Observable可以通过订阅来通知观察者。
铺垫了一下,就正式开始Observable的编写,Observable并不神秘,它就是一个函数,同时接收一个订阅函数。首先我们来定义Observable构造函数:
src/Observable.js
export default function Observable(subscribeFn) {
this.subscribeFn= subscribeFn
}
是不是很简单,subscribeFn定义了如何通知Observer,同时subscribeFn也要知道通知给哪一个Observer实例,因此自然subscribeFn接受一个Observer实例,即(observer: Observer):subscription,这里subscription一般来说是终止observer监听,有时也会包括终止事件产生,例如interval定时器,这块我们稍后再说。在RxJS是通过Observable.prototype.subscribe来接收observer实例的,或者接收Observer三要素onNext, onError, onComplete来实例化Observer,下面我们来定义subscribe函数:
import Observer from './Observer'
export default function Observable(subscribeFn) {
this.subscribeFn = subscribeFn
}
Observable.prototype.subscribe = function() {
let observer, subscription
if(arguments.length === 1) { // 传入的是Observer实例
observer = arguments[0]
} else {
observer = new Observer(arguments[0], arguments[1], arguments[2])
}
subscription = this.subscribeFn(observer)
return ()=> observer.unsubscribe()
}
现在我们来定义Observer,RxJS提供了三种通知Observer实例的方式,即next、error、complete,当next通知被调用时,我们用来描述Observer的onNext函数就会接收到通知信息,下面我们来实现Observer:
src/Observer.js
export default function Observer(onNext, onError, onComplete) {
this.onNext = onNext
this.onError = onError
this.onComplete = onComplete
this.unsubscribed = false
}
Observer.prototype.next = function(value) {
if(!this.unsubscribed) {
try {
this.onNext && this.onNext(value)
} catch(err) {
throw new Error(err)
}
}
}
Observer.prototype.error= function(err) {
if(!this.unsubscribed) {
try{
this.onError && this.onError(err)
} catch(err) {
throw new Error(err)
} finally {
this.unsubscribe()
}
}
}
Observer.prototype.complete = function() {
if(!this.unsubscribed) {
try {
this.onComplete && this.onComplete()
} catch(err) {
throw new Error(err)
} finally {
this.unsubscribe()
}
}
}
Observer.prototype.unsubscribe = function() {
this.unsubscribed = true
}
代码还是比较容易理解的,这里unsubscribed是一个标记,当ubsubscribed为true时,Observer实例将不会再收到通知。下面我们写一个小栗子这样会有一个更感性的理解,首先我们先写一个index.js将Observable和Observer暴露出来
src/observable/static/create.js
import Observable from './../../Observable'
export default function create(subscribeFn) {
return new Observable(subscribeFn)
}
src/observer/static/create.js
import Observer from './../../Observer.js'
export default function create(onNext, onError, onComplete) {
return new Observer(onNext, onError, onComplete)
}
src/index.js
import createObservable from './Observable/static/create'
import createObserver from './Observer/static/create'
const Rx = {}
Rx.Observable = {
create: createObservable
}
Rx.Observer = {
create: createObserver
}
module.exports = Rx
OK,下面上一个小栗子:
examples/01.basic.html
<script>
const observable = Rx.Observable.create((observer) => {
observer.next(1)
observer.next(2)
observer.next(3)
observer.next(4)
observer.complete()
})
const observer = Rx.Observer.create(
(value) => value % 2 === 0 && console.log(value),
(err) => console.log(err),
()=> console.log('completed!')
)
observable.subscribe(observer)
</script>
&amp;amp;amp;lt;img src="https://pic3.zhimg.com/v2-1c72c5dd2b7720a7c91788335dc36d02_b.png" data-rawwidth="418" data-rawheight="96" class="content_image" width="418"&amp;amp;amp;gt;cool,我们已经打下了一个不错的基础,RxJS强大之处在于提供了许多奇妙的API,API分为两大类,静态API和实例API,那么下面我们就分别挑两个简单的API来实现,进一步加深对RxJS的理解。
cool,我们已经打下了一个不错的基础,RxJS强大之处在于提供了许多奇妙的API,API分为两大类,静态API和实例API,那么下面我们就分别挑两个简单的API来实现,进一步加深对RxJS的理解。
我们先挑两个软柿子来捏,静态函数interval和实例函数take(在RxJS叫operator)。
静态函数其实就是一个new Observable的过程,也就是从0到1生成一个Observable。
src/observable/static/interval.js
export default function interval(period) {
let subscribeFn = (observer) => {
let intervalId = window.interval(() => {
observer.next()
})
return ()=> {
window.clearInterval(intervalId)
}
}
return new Observable(subscribeFn)
interval也非常容易理解,但这里有个问题,我们把Observable.js的代码再贴一遍
src/Observable.js
import Observer from './Observer'
export default function Observable(subscribeFn) {
this.subscribeFn = subscribeFn
}
Observable.prototype.subscribe = function() {
let observer, subscription
if(arguments.length === 1 && arguments[0] instanceof Observer) { // 传入的是Observer实例
observer = arguments[0]
} else {
observer = new Observer(arguments[0], arguments[1], arguments[2])
}
subscription = this.subscribeFn(observer)
return ()=> observer.unsubscribe()
}
可以看到这里我们直接返回了一个函数,保证我们在调用Observable实例.subscribe()的返回值可以终止事件通知。但这里我们忽略了subscribeFn的返回值,在interval中不仅仅要终止observer的订阅,还要取消定时器,那么当前Observable的实现是不能满足的,因此我们要在subscription上做些文章了,这里为了应对后面还会出现的场景,这里对Observer.prototype.unsubscribe做一点小的职责拆分,将其他需要伴随unsubscribe的事情放到unsubscribeFn中去,它并不是一定要存在的,因此我们将其作为Observer实例的一个属性来定义,同时增加一个新的构造函数Disposable,类似interval这样需要"清理现场"的工作可以放到Disposable的构造参数中,记住它是lazy的,这个构造参数是一个函数,这样我们可以通过判断subscribeFn的返回值是不是Disposable实例来决定是否做额外的事情。现在我们对Observable.js,Observer.js以及interval.js做一点修改,同时增加Disposable.js。
src/Disposable.js
export default function Disposable(disposeFn) {
this.disposeFn = disposeFn
}
Disposable.prototype.dispose = function() {
if(this.disposable instanceof Disposable) {
Disposable.dispose()
} else {
this.disposeFn()
}
}
src/Observable.js
import Observer from './Observer'
import Disposable from './Disposable'
export default function Observable(subscribeFn) {
this.subscribeFn = subscribeFn
}
Observable.prototype.subscribe = function() {
let observer, subscription
if(arguments.length === 1) { // 传入的是Observer实例
observer = arguments[0]
} else {
observer = new Observer(arguments[0], arguments[1], arguments[2])
}
subscription = this.subscribeFn(observer)
if(subscription instanceof Disposable) {
observer.unsubscribeFn = function() {
subscription.dispose()
}
}
return ()=> observer.unsubscribe()
}
src/Observer.js
// 略...
Observer.prototype.unsubscribe = function() {
this.unsubscribed = true
this.unsubscribeFn && this.unsubscribeFn()
}
src/observable/static/interval.js
import Disposable from './../../Disposable'
import Observable from './../../Observable'
export default function interval(period) {
let subscribeFn = (observer) => {
let count = 0
let intervalId = window.setInterval(() => {
observer.next(count++)
},period)
return new Disposable(()=> {
window.clearInterval(intervalId)
})
}
return new Observable(subscribeFn)
}
,下面我们来写一个operator实例函数take,operator就一面哈哈镜,当一个observable实例经过这面镜子就会生产另外一个observable,切记是两个完全不相同的observable实例。take的签名是(count):Observable,即订阅take所产生的Observable实例只能获取到count次通知。下面我们来试着实现我们的第一个operator函数take。
src/observable/take.js
import Disposable from './../Disposable'
import Observable from './../Observable'
Observable.prototype.take = function (count) {
const subscribeFn = (observer) => {
const unsubscribe = this.subscribe(
value => {
if (count > 0) {
observer.next(value)
count--
} else {
observer.complete()
}
},
err => observer.error(err),
() => observer.complete()
)
return new Disposable(() => {
unsubscribe()
})
}
return new Observable(subscribeFn)
}
我们写一个小栗子来测试一下先,
<script>
const app$ = Rx.Observable.interval(1000).take(3)
app$.subscribe((x) => {
console.log('x: ' + x)
})
</script>
&amp;amp;amp;lt;img src="https://pic3.zhimg.com/v2-895c2ec4e80b3901ff1fbb3be6a90d1e_b.png" data-rawwidth="359" data-rawheight="130" class="content_image" width="359"&amp;amp;amp;gt;OK,是我们想要的效果:),现在我们再试试可否从过程中终止监听。
OK,是我们想要的效果:),现在我们再试试可否从过程中终止监听。 <body>
<button id='unsubscribeTake'>unsubscribe take</button>
<script src='/dist/rx-simple.js'></script>
<script>
const unsubscribeTakeDom = document.getElementById('unsubscribeTake')
const app$ = Rx.Observable.interval(1000).take(3)
const unsubscribe = app$.subscribe((x) => {
console.log('x: ' + x)
})
unsubscribeTakeDom.onclick = function() {
unsubscribe()
}
</script>
</body>
&amp;amp;amp;lt;img src="https://pic1.zhimg.com/v2-c315ee2db68bd2a9497cda8896d645f4_b.png" data-rawwidth="389" data-rawheight="307" class="content_image" width="389"&amp;amp;amp;gt;OK,恰然而止:)
OK,恰然而止:)
第一期我们完成了Observable.js,Observer.js,Disposable.js,以及包括create,range,interval,take几个API,我们已经有了一个不错的开始,后面我们会实现更多复杂的API,从这些API你可以更好的理解RxJS在什么场景下会发挥威力,同时现有代码也会逐渐暴露出问题,相信在不断解决问题的过程中大家会对RxJS有一个更好的认识,下节我们将一起实现concont,merge,switch,mergeMap,takeUtil等API,是的,下节更精彩~