本文主要介绍了一些转换功能的操作符, 并且引入一些例子便于开发者快速理解和应用它们.
先介绍如下几种具有转换功能的操作符:
-
toArray: 将序列中的元素装入一个数组, 作为新序列的一个元素
-
map 操作符: 映射操作, 和 swift 集合中的同理.
-
flatMap 族操作符: 类似 swift 中的集合上可以进行的 flatMap, 即平铺映射.
可以将由 Observable 组成的序列中的每个 Observable 都展开, 然后合并到一个 Observable 上.
如果这些作为元素的 Observable 序列有新的事件, 则观察者也会被通知到. 总而言之, 就是 flatMap 总是会将每个子序列中的任何改变送入新序列.
这个行为可以很好地被用在网络请求中, 在网络请求操作序列上直接应用 flatMap, 也可以达到 “等待” 下一个元素的效果.(实际就是当下一个元素到达时, 订阅者可以收到这些事件, 而非真的去等待事件到达.)
- flatMapLatest 操作符: 可以进行平铺映射
和 flatMap 相比, 它的一个巨大不同是: 当有多个子 Observable 序列时, 它会自动将订阅切换到最后一个产生事件的序列上.
这里的最后一个发射的序列是指: 在原始的 Observable 上的最新的那个作为元素的子序列.
这个操作符在网络请求上十分常用, 比如用户搜索时, 将每个搜索的网络请求都作为一个子序列放到原始序列中, 这样的话, 订阅者可以自动切换到最新的搜索上, 而不用去管之前的结果.
- materialize 操作符: 将序列中的事件包裹起来, 原样呈现给订阅者, 而非事件中包含的元素, 这样就可以提前对子序列中的事件进行处理.
它非常适用于对子序列没有直接控制的情况.(比如网络请求操作…)
- dematerialize: 将序列中的事件包含重新转换为元素, 作用和 materialize 正好相反.
示例代码如下所示:
func transformingOperatorIntro() { /* 1. toArray: 将序列中的元素装入一个数组, 作为新序列的一个元素 */ exampleOf(msg: "toArray 操作符", action: { bag in Observable.of("a", "b", "c") .toArray() .subscribe(onNext: { print($0) print(type(of: $0)) }, onError: { print($0) }, onCompleted: { print("完成") }, onDisposed: { print("被释放")}) .disposed(by: bag) }) /* 2. map 操作符: 映射操作, 和 swift 集合中的同理. */ exampleOf(msg: "map 操作符", action: { bag in Observable.of(1, 2, 3, 4, 5) .map({ "元素 \($0 * 2)" }) .subscribe(onNext: { print($0) }).disposed(by: bag) }) /* 3. flatMap 族操作符: 类似 swift 中的集合上可以进行的 flatMap, 即平铺映射. 可以将由 Observable 组成的序列中的每个 Observable 都展开, 然后合并到一个 Observable 上. 如果这些作为元素的 Observable 序列有新的事件, 则观察者也会被通知到. 总而言之, 就是 flatMap 总是会发射每个子序列中的任何改变. 这个行为可以很好地被用在网络请求中, 在网络请求序列上直接应用 flatMap, 也可以达到 "等待" 下一个元素的效果. (实际就是当下一个元素到达时, 订阅者可以收到这些事件) */ exampleOf(msg: "flatMap 族操作符", action: { bag in struct Student { var score: BehaviorSubject<Int> } let stu1 = Student(score: BehaviorSubject<Int>(value: 80)) let stu2 = Student(score: BehaviorSubject<Int>(value: 95)) let pub = PublishSubject<Student>() pub.flatMap({ $0.score }).subscribe(onNext: { print("成绩: \($0)") }).disposed(by: bag) pub.onNext(stu1) pub.onNext(stu2) stu1.score.onNext(99) }) /* 4. flatMapLatest 操作符: 可以进行平铺映射 它的一个巨大不同是: 当有多个子 Observable 序列时, 它会自动将订阅切换到最后一个产生事件的序列上. 可以看到和之前的 flatMap 最大的不同是: 它在平铺映射的同时, 还进行了切换操作, 将订阅切换到最后一个发射的序列上. 这里的最后一个发射的序列是指: 在原始的 Observable 上的最新的那个作为元素的子序列. 故上述代码中, 订阅会一直订阅的是 stu2, 因为 stu2 是最新的一个子序列. 这个操作符在网络请求上仍然十分常用, 比如用户搜索时, 将每个搜索的网络请求都作为一个子序列放到原始序列中, 这样的话, 订阅者可以自动切换到最新的搜索上, 而不用去管之前的结果. */ exampleOf(msg: "flatMapLatest 族操作符", action: { bag in struct Student { var score: BehaviorSubject<Int> } let stu1 = Student(score: BehaviorSubject<Int>(value: 80)) let stu2 = Student(score: BehaviorSubject<Int>(value: 95)) let pub = PublishSubject<Student>() pub.flatMapLatest({ $0.score }).subscribe(onNext: { print("成绩: \($0)") }).disposed(by: bag) pub.onNext(stu1) pub.onNext(stu2) stu1.score.onNext(99) stu2.score.onNext(15) stu1.score.onNext(70) }) /* 5. materialize 操作符: 将序列中的事件原样呈现给订阅者, 而非事件中包含的元素. 非常适用于对子序列没有直接控制的情况.(比如网络请求操作...) */ exampleOf(msg: "materialize 操作符: ", action: { bag in enum MyError: Error { case badScore case unknown } struct Student { var score: BehaviorSubject<Int> } let stu1 = Student(score: BehaviorSubject<Int>(value: 80)) let stu2 = Student(score: BehaviorSubject<Int>(value: 95)) let pub = PublishSubject<Student>() pub.flatMapLatest({ // 应用了 materialize, 则可以在 next 观察中对子序列的事件进行对应处理. $0.score.materialize() }).subscribe(onNext: { switch $0 { case .next(let elem): print("成绩: \(elem)") case .error(let error): print("子序列出错: \(error)") case .completed: print("子序列完成") } }, onError: { print($0) }).disposed(by: bag) pub.onNext(stu1) pub.onNext(stu2) stu1.score.onNext(99) stu2.score.onError(MyError.badScore) stu2.score.onNext(15) // 不会收到 stu1.score.onNext(70) }) /* 6. dematerialize: 将序列中的事件包含重新转换为元素. */ exampleOf(msg: "dematerialize 操作符", action: { bag in enum MyError: Error { case badScore case unknown } struct Student { var score: BehaviorSubject<Int> } let stu1 = Student(score: BehaviorSubject<Int>(value: 80)) let stu2 = Student(score: BehaviorSubject<Int>(value: 95)) let pub = PublishSubject<Student>() pub.flatMapLatest { $0.score.materialize() }.filter({ // 将子序列中的 error 先进行处理 guard $0.error == nil else { print($0.error.debugDescription) return false } return true }) .dematerialize() // 反向处理, 将元素复原. .subscribe(onNext: {// 这样就不需要再在订阅时使用 switch 来判断了 print($0) }, onError: { print($0) }).disposed(by: bag) pub.onNext(stu1) pub.onNext(stu2) stu1.score.onNext(99) stu2.score.onError(MyError.badScore) stu2.score.onNext(15) // 不会收到 stu1.score.onNext(70) }) }