RxSwift_操作符_map、flatmap、flatMapLatest

map操作符將源Observable的每個元素,通過提供的方法轉(zhuǎn)換,然后返回含有轉(zhuǎn)換后元素的Observable

#案例1:

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

/// 打印結(jié)果:
/// 10
/// 20
/// 30

flatMap 操作符會對源Observable的每一個元素應(yīng)用一個轉(zhuǎn)換方法,將他們轉(zhuǎn)換成Observable,然后將這些Observable的元素合并之后再發(fā)送出來,即將其降維成一個Observable序列

#案例2:

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

/// 打印結(jié)果:
RxSwift.(unknown context at $10dc3e738).Just<Swift.Int>
RxSwift.(unknown context at $10dc3e738).Just<Swift.Int>
RxSwift.(unknown context at $10dc3e738).Just<Swift.Int>

/// 通過flatMap降維,獲取內(nèi)部的元素值
Observable.of(1,2,3)
    .map { value in
        return Observable.just(value * 10)
    }
    .flatMap({
        return $0
    })
    .subscribe(onNext:{
        print($0)
    })
    .disposed(by: bag)

    }

/// 打印結(jié)果:
/// 10
/// 20
/// 30
#案例3:

# 這種寫法也是可以的,為什么? 
# 請看下方講解
Observable.of(1,2,3)
    .flatMap { (value) -> Observable<Int> in
        return Observable.just(value * 10)
    }
    .subscribe(onNext:{
        print($0)
   })
   .disposed(by: bag)

/// 打印結(jié)果:
/// 10
/// 20
/// 30
/// 源碼解析:
/// 首先selector函數(shù)型參數(shù)返回的是一個可觀察對象,因?yàn)楦鶕?jù)Source.Element可知,
/// 閉包函數(shù)返回的是一個可觀察對象,如果此閉包函數(shù)返回的不是一個可觀察對象則會報錯
///
/// 第二:
/// selector閉包函數(shù)入?yún)elf.Element,可以是普通的數(shù)據(jù)類型,也可以是可觀察對象
/// 如果是普通數(shù)據(jù)類型,那么就必須要在閉包函數(shù)中將其包裝為可觀察對象
/// 如果其本身就是可觀察對象,那么可以直接返回,不需要再包裝
public func flatMap<Source>(_ selector: @escaping (Self.Element) throws -> Source) 
-> RxSwift.Observable<Source.Element> 
where Source : RxSwift.ObservableConvertibleType

flatMapLatest: 當(dāng)源序列有新的事件發(fā)生的時候,flatMapLatest會自動取消上一個是事件的訂閱,轉(zhuǎn)到新的事件的訂閱上面,而flatMap則會訂閱全部

#案例4:

# 這種情況,flatMapLatest 與 flatMap 沒什么不同,不能體現(xiàn)兩者的區(qū)別
Observable.of(1,2,3)
    .flatMapLatest { (value) -> Observable<Int> in  
      ///  Observable.just 是一次性的可觀察序列,發(fā)生完Event后,就直接Complete結(jié)束
      /// 不能再發(fā)送第二個次的Event
      /// 所以這不能很好的體現(xiàn)flatMapLatest的主要功能:取消上一次的事件訂閱事件
        return Observable.just(value * 10)
    }
    /// subscribe訂閱的不是Observable.of(1,2,3)創(chuàng)建的可觀察序列
    /// 而是flatMap轉(zhuǎn)變后的可觀察序列
    .subscribe(onNext:{
        print($0)
    })
    .disposed(by: bag)

/// 打印結(jié)果:
/// 10
/// 20
/// 30

flatMapLatest 與 flatMap 使用區(qū)別案例

struct Player {
    var score : BehaviorRelay<Int>    
}

flatMap的實(shí)現(xiàn)

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

let players = PublishSubject<Player>()

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

/// players 發(fā)送Event,對象是aPlayer,因此aPlayer被訂閱了
players.onNext(aPlayer)
/// aPlayer對象,發(fā)出事件,因?yàn)楸挥嗛喠?,所以能接收到并打?aPlayer.score.accept(3)

/// players 再次發(fā)出Event,對象是bPlayer,因此bPlayer也被訂閱了
/// 但是aPlayer的訂閱并沒有被取消
players.onNext(bPlayer)

/// aPlayer對象再發(fā)出事件,依然能被接收和打印
aPlayer.score.accept(4)

/// 打印結(jié)果:
1
3
2
4

flatMapLatest的實(shí)現(xiàn)

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

let players = PublishSubject<Player>()

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

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

/// players發(fā)起一個新的事件,會對bPlayer訂閱,并且同時取消上一次Event發(fā)送過來的aPlayer對象
players.onNext(bPlayer)

/// aPlayer被取消訂閱了,所以沒有打印
aPlayer.score.accept(4)

/// 打印結(jié)果:
1
3
2

總結(jié):如果flatMap接收事件Event包裹的元素也是一個Observable,并且不希望保存上一次的訂閱,則可以使用flatMapLatest

BehaviorRelay 、Subject的使用,可以參考這篇文章

勘誤:map、flatMap、flatMapLatest最終返回的都是一個可觀察序列(比如Observable、Driver),
不同的是map閉合函數(shù)返回的值,map函數(shù)會自動包裝一層可觀察序列
flatMap、flatMapLatest閉合函數(shù)返回的必須是一個可觀察序列,
與map作用最大的區(qū)別是如果源可觀察序列攜帶的也是可觀察序列元素,那么flatMap、flatMapLatest就可以將閉合函數(shù)入?yún)⒌目捎^察序列直接返回,這樣達(dá)到一個“降維”的效果,訂閱flatMap、flatMapLatest轉(zhuǎn)換過后,就能直接獲取到可觀察序列中的元素(非可觀察序列),如有不明的請仔細(xì)查看案例2中的代碼


項(xiàng)目實(shí)操

看了很多篇文章,一直說flapMap、flapMapLatest很好用,但是一直都沒說要怎么用,那到底要怎么用了?(學(xué)以致用很重要)

首先要知道的是flapMapflapMapLatest返回的是一個可觀察序列Observable,一個可觀察序列,相等是一個“輸出源”,可以進(jìn)行訂閱監(jiān)聽,根據(jù)不同的輸出做不同的處理

繼續(xù)以登錄界面為例:一個手機(jī)號,一個驗(yàn)證碼,一個登錄按鈕,實(shí)現(xiàn)點(diǎn)擊登錄按鈕,發(fā)起網(wǎng)絡(luò)請求

# 未改進(jìn)的代碼:有一個最大的問題,請看代碼注釋

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)
}

/// 直接訂閱登錄按鈕的點(diǎn)擊事件,在訂閱中直接發(fā)起請求
/// 問題:不能優(yōu)雅的將網(wǎng)絡(luò)請求結(jié)果反饋到UI層中,將UI刷新。
/// 可能會通過多創(chuàng)建幾個Subject或可觀察序列的方式來實(shí)現(xiàn)反饋,這樣很多余,并且把問題復(fù)雜化
loginAction
    .withLatestFrom(userNameAndCode)
    .subscribe(onNext:{ (tel,code) in
        /// 發(fā)起網(wǎng)絡(luò)請求
    })
    .disposed(by: disposebag)

# 改進(jìn)后的代碼
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)
}


/// 優(yōu)點(diǎn):
/// 第一:在flatMapLatest序列轉(zhuǎn)換的時候,就發(fā)起網(wǎng)絡(luò)請求
/// 第二:flatMapLatest序列轉(zhuǎn)換后的結(jié)果,必定是一個Observable可觀察序列,將這個序列拋到UI層訂閱,就能直接的將網(wǎng)絡(luò)請求結(jié)果反饋到UI中刷新處理

let loginResutl = loginAction.
        .withLatestFrom(userNameAndCode)
        .flatMapLatest { (arg0) -> Observable<String> in
            /// 發(fā)起網(wǎng)絡(luò)請求,然后將結(jié)果包裹成Observable再發(fā)送出去
            et (a, b) = arg0
            return Observable.just("\(a)\(b)")
        }

提示:
(1)Observable 與 Driver 對于 連著使用withLatestFrom、flatMapLatest操作符是不太一樣的請看下方
(2)對于:不會產(chǎn)生error事件、在主線程監(jiān)聽、并且共享狀態(tài)變化的極力推薦使用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閉合函數(shù)要求返回的是 :RxCocoa.SharedSequence,也就是Driver對象
    /// Observable中flatMapLatest閉合函數(shù)要求非的是 :RxSwift.Observable
    .flatMapLatest { (arg0) -> Driver<String> in
        return Observable<String>.just("網(wǎng)絡(luò)請求結(jié)果").asDriver(onErrorJustReturn: "網(wǎng)絡(luò)異常")
    }

PS:對于完整的登錄功能Demo,要本人再多幾次的review之后一定會出一篇文章,敬請期待??!


2021年3月19日更新
flatMap 與 map 的重要不同,flatMap可以起到一個降維的作用(flatMap與map的配合使用可以達(dá)到不錯的效果)

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

/// 打印結(jié)果:
/// flatMap輸出:RxSwift.(unknown context at $102f5ccb8).Just<Swift.Int>
/// map輸出:10
/// flatMap輸出:RxSwift.(unknown context at $102f5ccb8).Just<Swift.Int>
/// map輸出:20
/// flatMap輸出:RxSwift.(unknown context at $102f5ccb8).Just<Swift.Int>
/// map輸出:30
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容