接上回书:
关于操作符"shareReplay"
假如你需要多个观察者(observer
)共享同一个订阅(subscription
),那么你就要确定两件事情
- 如果一个序列被一个新的订阅者订阅了,那该如何处理之前已经被接收的元素呢?(重播最后一个元素,还是所有的元素,亦或者是最后n个)
- 如何决定何时触发被共享的订阅(subscripiton)呢?
通常情况下你会使用到shareReplay
:
let counter = myInterval(0.1)
.shareReplay(1)
print("Started ----")
let subscription1 = counter
.subscribe(onNext: { n in
print("First \(n)")
})
let subscription2 = counter
.subscribe(onNext: { n in
print("Second \(n)")
})
Thread.sleep(forTimeInterval: 0.5)
subscription1.dispose()
Thread.sleep(forTimeInterval: 0.5)
subscription2.dispose()
print("Ended ----")
打印结果:
//Started ----
Subscribed
First 0
Second 0
First 1
Second 1
First 2
Second 2
First 3
Second 3
First 4
Second 4
First 5
Second 5
Second 6
Second 7
Second 8
Second 9
Disposed
Ended ----
shareReplay
,多个订阅者共享结果,就像您看到的,使用了该操作符的序列,在有多个订阅者订阅的时候,并不会调用多次之前构造myInterval
时候闭包里的代码(结果只打印了一次subscribed,Disposed事件).为了更好地理解shareReplay
,参照这篇文章给出的例子:
let sequenceOfInts = PublishSubject()
let a = sequenceOfInts.map{ i -> Int in
print("MAP---\(i)")
return i * 2
}.shareReplay(1)
let b = a.subscribe(onNext:{
print("--1--\($0)")
})
sequenceOfInts.on(.next(1))
sequenceOfInts.on(.next(2))
let c = a.subscribe (onNext: {
print("--2--\($0)")
})
sequenceOfInts.on(.next(3))
sequenceOfInts.on(.next(4))
let d = a.subscribe (onNext:{
print("--3--\($0)")
})
sequenceOfInts.on(.completed)
快去看看使用和不使用shareReplay
打印的结果吧!
注: shareReplay的参数代表了共享最后几次结果.想象一下该操作符的实际运用场景,最直观的就是网络请求,如果多个订阅者订阅了某个网络请求,我们没必要去重复的请求网络,这时候就有了shareReplay的用武之地了!
下面是HTTP请求在Rx中的范例,这其实跟interval操作符很像:
extension Reactive where Base: URLSession {
public func response(_ request: URLRequest) -> Observable<(Data, HTTPURLResponse)> {
return Observable.create { observer in
let task = self.dataTaskWithRequest(request) { (data, response, error) in
guard let response = response, let data = data else {
observer.on(.error(error ?? RxCocoaURLError.Unknown))
return
}
guard let httpResponse = response as? HTTPURLResponse else {
observer.on(.error(RxCocoaURLError.nonHTTPResponse(response: response)))
return
}
observer.on(.next(data, httpResponse))
observer.on(.completed)
}
task.resume()
return Disposables.create {
task.cancel()
}
}
}
}
操作符(Operators)
在RxSwift的世界里,有众多的操作符可供选择,你可以在这里找到所有的操作符使用说明.你也可以在RxSwift Demo的PlayGround里看到几乎所有使用到的操作符的演示,快去试试吧!
自定义操作符(Custom Operators)
让我们来动手实现一个自己的map操作符myMap吧!
extension ObservableType {
func myMap(transform: @escaping (E) -> R) -> Observable {
return Observable.create { observer in
let subscription = self.subscribe { e in
switch e {
case .next(let value):
let result = transform(value)
observer.on(.next(result))
case .error(let error):
observer.on(.error(error))
case .completed:
observer.on(.completed)
}
}
return subscription
}
}
}
好吧,很清爽,一个转换函数(transform
)作为参数,返回一个Observable,咦,等等,好像这个家伙跟Swift数组的map方法很像啊,好像做的事情一毛一样;数组的元素被转换,得到的是另一个数组,Observable的value值被转换,得到另外的一个Observable.你完全可以把Observable想象成Swfit数组嘛,他们都是容器.
现在你可以使用自己的myMap方法了:
let subscription = myInterval(0.1)
.myMap { e in
return "This is simply \(e)"
}
.subscribe(onNext: { n in
print(n)
})
打印结果:
//Subscribed
This is simply 0
This is simply 1
This is simply 2
This is simply 3
This is simply 4
This is simply 5
This is simply 6
This is simply 7
This is simply 8
...
错误处理
Observable中的异步处理错误机制
如果一个序列以错误终止,那么所有依赖序列同样会以错误终止,参照短路逻辑.
你可以用catch操作符捕捉Observable序列的error.你也可以使用Retry操作符把error信号过滤掉.
调试编译器的错误
当你书写优雅的RxSwift或者RxCocoa代码的时候,你很大程度上会依赖编译器来推断Observables的类型,这也是Swift真正优秀的地方,但是,这有时也会带来一些苦恼.
images = word
.filter { $0.containsString("important") }
.flatMap { word in
return self.api.loadFlickrFeed("karate")
.catchError { error in
return just(JSON(1))
}
}
如果编译器报出错误(未知的类型),那么我建议你首先在闭包里注明闭包的返回值.例如:
images = word
.filter { s -> Bool in s.containsString("important") }
.flatMap { word -> Observable in
return self.api.loadFlickrFeed("karate")
.catchError { error -> Observable in
return just(JSON(1))
}
}
如果还不行,那干脆这样:
images = word
.filter { (s: String) -> Bool in s.containsString("important") }
.flatMap { (word: String) -> Observable in
return self.api.loadFlickrFeed("karate")
.catchError { (error: Error) -> Observable in
return just(JSON(1))
}
}
注: 其实笔者还是建议初学者写出完整的闭包语法,这既有助于理解代码,闭包类型又一目了然.但是,显然这样还有失优雅.
代码调试
你可以单独使用调试器,但大多时候使用debug
操作符是更高效的,你可以这样做:
let subscription = myInterval(0.1)
.debug("my probe")
.map { e in
return "This is simply \(e)"
}
.subscribe(onNext: { n in
print(n)
})
Thread.sleepForTimeInterval(0.5)
subscription.dispose()
打印结果:
//
[my probe] subscribed
Subscribed
[my probe] -> Event next(Box(0))
This is simply 0
[my probe] -> Event next(Box(1))
This is simply 1
[my probe] -> Event next(Box(2))
This is simply 2
[my probe] -> Event next(Box(3))
This is simply 3
[my probe] -> Event next(Box(4))
This is simply 4
[my probe] dispose
Disposed
当然,你同样可以很轻松的自定义你自己的debug
操作符:
extension ObservableType {
public func myDebug(identifier: String) -> Observable {
return Observable.create { observer in
print("subscribed \(identifier)")
let subscription = self.subscribe { e in
print("event \(identifier) \(e)")
switch e {
case .next(let value):
observer.on(.next(value))
case .error(let error):
observer.on(.error(error))
case .completed:
observer.on(.completed)
}
}
return Disposables.create {
print("disposing \(identifier)")
subscription.dispose()
}
}
}
}
后话: 笔者终于在大年三十晚上听着掏粪男神们不知道真唱假唱的<<美丽中国年>>中完成了RxSwift文档翻译工作的中篇,不过好像气氛有点不对劲呢...只有我一个人在怀疑这是不是在过年吗?
未完待续...