RxSwift_操作符_map、flatmap、flatMapLatest

map操作符将源Observable的每个元素,通过提供的方法转换,然后返回含有转换后元素的Observable

#案例1:

Observable.of(1,2,3)
    .map{
        $0 * 10
    }
    .subscribe(onNext:{
        print($0)
    })
    .disposed(by: bag)

/// 打印结果:
/// 10
/// 20
/// 30

flatMap 操作符会对源Observable的每一个元素应用一个转换方法,将他们转换成Observable,然后将这些Observable的元素合并之后再发送出来,即将其降维成一个Observable序列

#案例2:

/// 如果Observable中的元素也是Observable
Observable.of(1,2,3)
    .map { value in
        return Observable.just(value * 10)
    }
    .subscribe(onNext:{
        print($0)
    })
    .disposed(by: bag)

/// 打印结果:
RxSwift.(unknown context at $10dc3e738).Just
RxSwift.(unknown context at $10dc3e738).Just
RxSwift.(unknown context at $10dc3e738).Just

/// 通过flatMap降维,获取内部的元素值
Observable.of(1,2,3)
    .map { value in
        return Observable.just(value * 10)
    }
    .flatMap({
        return $0
    })
    .subscribe(onNext:{
        print($0)
    })
    .disposed(by: bag)

    }

/// 打印结果:
/// 10
/// 20
/// 30
#案例3:

# 这种写法也是可以的,为什么? 
# 请看下方讲解
Observable.of(1,2,3)
    .flatMap { (value) -> Observable in
        return Observable.just(value * 10)
    }
    .subscribe(onNext:{
        print($0)
   })
   .disposed(by: bag)

/// 打印结果:
/// 10
/// 20
/// 30
/// 源码解析:
/// 首先selector函数型参数返回的是一个可观察对象,因为根据Source.Element可知,
/// 闭包函数返回的是一个可观察对象,如果此闭包函数返回的不是一个可观察对象则会报错
///
/// 第二:
/// selector闭包函数入参Self.Element,可以是普通的数据类型,也可以是可观察对象
/// 如果是普通数据类型,那么就必须要在闭包函数中将其包装为可观察对象
/// 如果其本身就是可观察对象,那么可以直接返回,不需要再包装
public func flatMap(_ selector: @escaping (Self.Element) throws -> Source) 
-> RxSwift.Observable 
where Source : RxSwift.ObservableConvertibleType

flatMapLatest: 当源序列有新的事件发生的时候,flatMapLatest会自动取消上一个是事件的订阅,转到新的事件的订阅上面,而flatMap则会订阅全部

#案例4:

# 这种情况,flatMapLatest 与 flatMap 没什么不同,不能体现两者的区别
Observable.of(1,2,3)
    .flatMapLatest { (value) -> Observable in  
      ///  Observable.just 是一次性的可观察序列,发生完Event后,就直接Complete结束
      /// 不能再发送第二个次的Event
      /// 所以这不能很好的体现flatMapLatest的主要功能:取消上一次的事件订阅事件
        return Observable.just(value * 10)
    }
    /// subscribe订阅的不是Observable.of(1,2,3)创建的可观察序列
    /// 而是flatMap转变后的可观察序列
    .subscribe(onNext:{
        print($0)
    })
    .disposed(by: bag)

/// 打印结果:
/// 10
/// 20
/// 30

flatMapLatest 与 flatMap 使用区别案例

struct Player {
    var score : BehaviorRelay    
}

flatMap的实现

let aPlayer = Player(score: BehaviorRelay(value: 1))
let bPlayer = Player(score: BehaviorRelay(value: 2))

let players = PublishSubject()

players.asObserver()
    .flatMap { (player) -> Observable in
        player.score.asObservable()
    }
    /// subscribe 订阅的不是players
    /// 而是players事件中包含的每一个player.score
    .subscribe(onNext:{
        print($0)
    })
    .disposed(by: bag)

/// players 发送Event,对象是aPlayer,因此aPlayer被订阅了
players.onNext(aPlayer)
/// aPlayer对象,发出事件,因为被订阅了,所以能接收到并打印
aPlayer.score.accept(3)

/// players 再次发出Event,对象是bPlayer,因此bPlayer也被订阅了
/// 但是aPlayer的订阅并没有被取消
players.onNext(bPlayer)

/// aPlayer对象再发出事件,依然能被接收和打印
aPlayer.score.accept(4)

/// 打印结果:
1
3
2
4

flatMapLatest的实现

let aPlayer = Player(score: BehaviorRelay(value: 1))
let bPlayer = Player(score: BehaviorRelay(value: 2))

let players = PublishSubject()

players.asObserver()
    .flatMapLatest { (player) -> Observable in
        player.score.asObservable()
    }
    .subscribe(onNext:{
        print($0)
    })
    .disposed(by: bag)

players.onNext(aPlayer)
aPlayer.score.accept(3)

/// players发起一个新的事件,会对bPlayer订阅,并且同时取消上一次Event发送过来的aPlayer对象
players.onNext(bPlayer)

/// aPlayer被取消订阅了,所以没有打印
aPlayer.score.accept(4)

/// 打印结果:
1
3
2

总结:如果flatMap接收事件Event包裹的元素也是一个Observable,并且不希望保存上一次的订阅,则可以使用flatMapLatest

BehaviorRelay 、Subject的使用,可以参考这篇文章

勘误:map、flatMap、flatMapLatest最终返回的都是一个可观察序列(比如Observable、Driver),
不同的是map闭合函数返回的值,map函数会自动包装一层可观察序列
flatMap、flatMapLatest闭合函数返回的必须是一个可观察序列
与map作用最大的区别是如果源可观察序列携带的也是可观察序列元素,那么flatMap、flatMapLatest就可以将闭合函数入参的可观察序列直接返回,这样达到一个“降维”的效果,订阅flatMap、flatMapLatest转换过后,就能直接获取到可观察序列中的元素(非可观察序列),如有不明的请仔细查看案例2中的代码


项目实操

看了很多篇文章,一直说flapMap、flapMapLatest很好用,但是一直都没说要怎么用,那到底要怎么用了?(学以致用很重要)

首先要知道的是flapMapflapMapLatest返回的是一个可观察序列Observable,一个可观察序列,相等是一个“输出源”,可以进行订阅监听,根据不同的输出做不同的处理

继续以登录界面为例:一个手机号,一个验证码,一个登录按钮,实现点击登录按钮,发起网络请求

# 未改进的代码:有一个最大的问题,请看代码注释

let telObservable = telInput.rx.text.orEmpty
let codeObservable = codeInput.rx.text.orEmpty
let tapObservable = loginBtn.rx.tap

let userNameAndCode = Observable<(String,String)>.combineLatest(telObservable, codeObservable) {
    return ($0,$1)
}

/// 直接订阅登录按钮的点击事件,在订阅中直接发起请求
/// 问题:不能优雅的将网络请求结果反馈到UI层中,将UI刷新。
/// 可能会通过多创建几个Subject或可观察序列的方式来实现反馈,这样很多余,并且把问题复杂化
loginAction
    .withLatestFrom(userNameAndCode)
    .subscribe(onNext:{ (tel,code) in
        /// 发起网络请求
    })
    .disposed(by: disposebag)

# 改进后的代码
let telObservable = telInput.rx.text.orEmpty
let codeObservable = codeInput.rx.text.orEmpty
let tapObservable = loginBtn.rx.tap

let userNameAndCode = Observable<(String,String)>.combineLatest(telObservable, codeObservable) {
    return ($0,$1)
}


/// 优点:
/// 第一:在flatMapLatest序列转换的时候,就发起网络请求
/// 第二:flatMapLatest序列转换后的结果,必定是一个Observable可观察序列,将这个序列抛到UI层订阅,就能直接的将网络请求结果反馈到UI中刷新处理

let loginResutl = loginAction.
        .withLatestFrom(userNameAndCode)
        .flatMapLatest { (arg0) -> Observable in
            /// 发起网络请求,然后将结果包裹成Observable再发送出去
            et (a, b) = arg0
            return Observable.just("\(a)\(b)")
        }

提示:
(1)Observable 与 Driver 对于 连着使用withLatestFrom、flatMapLatest操作符是不太一样的请看下方
(2)对于:不会产生error事件、在主线程监听、并且共享状态变化的极力推荐使用Driver替换Observable

let telObservable = telInput.rx.text.orEmpty.asDriver()
let codeObservable = codeInput.rx.text.orEmpty.asDriver()
let tapObservable = loginBtn.rx.tap.asDriver()

let userNameAndCode = Driver<(String,String)>.combineLatest(telObservable, codeObservable) {
    return ($0,$1)
}

let loginResutl = loginAction
    .withLatestFrom(userNameAndCode)
    /// Driver 中的flatMapLatest闭合函数要求返回的是 :RxCocoa.SharedSequence,也就是Driver对象
    /// Observable中flatMapLatest闭合函数要求非的是 :RxSwift.Observable
    .flatMapLatest { (arg0) -> Driver in
        return Observable.just("网络请求结果").asDriver(onErrorJustReturn: "网络异常")
    }

PS:对于完整的登录功能Demo,要本人再多几次的review之后一定会出一篇文章,敬请期待!!


2021年3月19日更新
flatMap 与 map 的重要不同,flatMap可以起到一个降维的作用(flatMap与map的配合使用可以达到不错的效果)

Observable.of(1,2,3)
    .map { value in
        return Observable.just(value * 10)
    }
    .flatMap({ value -> Observable in
        print("flatMap输出:\(value)")
        return value
    })
    .map({(value) -> Int in
        print("map输出:\(value)")
        return value
    })
    .subscribe(onNext:{ value in
    })
    .disposed(by: bag)

/// 打印结果:
/// flatMap输出:RxSwift.(unknown context at $102f5ccb8).Just
/// map输出:10
/// flatMap输出:RxSwift.(unknown context at $102f5ccb8).Just
/// map输出:20
/// flatMap输出:RxSwift.(unknown context at $102f5ccb8).Just
/// map输出:30

你可能感兴趣的:(RxSwift_操作符_map、flatmap、flatMapLatest)