RxSwift学习--高阶函数 / 操作符(上)

前言

RxSwift中,高阶函数也可以成为操作符,高阶函数可以帮助我们创建新的序列,或者变化组合原有的序列,从而生成一个新的序列。

转换操作符

1. map

map操作符会通过传入一个函数闭包把原来的 Observable 序列转变为一个新的 Observable 序列,map函数会将源序列的所有元素进行转换,返回一个新序列.


例子:

    let disposeBag = DisposeBag()
 
    Observable.of(1, 2, 3)
        .map { $0 * 100}
        .subscribe(onNext: { print($0) })
        .disposed(by: disposeBag)
    
    //结果:
    100
    200
    300

2. flatMap

map在做转换的时候容易出现“升维”的情况。即转变之后,从一个序列变成了一个序列的序列。而flatMap 操作符会对源Observable 的每一个元素应用一个转换方法,将他们转换成 Observables。 然后将这些 Observables 的元素合并之后再发送出来。即又将其 降维成一个 Observable 序列。


这个操作符是非常有用的。比如当 Observable 的元素本身就拥有其他的 Observable 时,我们可以将所有子 Observables 的元素发送出来。

例子:

    let disposeBag = DisposeBag()
    let Henry = BehaviorSubject(value: 1)
    let Jeannie = BehaviorSubject(value: 2)
    let Henry_Subject = BehaviorSubject(value: Henry)

    Henry_Subject.asObservable()
          .flatMap { $0 }
          .subscribe(onNext: { print($0) })
          .disposed(by: disposeBag)
    Henry.onNext(3)
    Henry_Subject.onNext(Jeannie)
    Henry.onNext(5)
    Jeannie.onNext(6)
    
    //结果
    1
    3
    2
    5
    6

3. flatMapLatest

flatMapLatest操作符会将Observable 的元素转换成其他的 Observable,然后取这些 Observables 中最新的一个。


flatMapLatest操作符将源Observable 的每一个元素应用一个转换方法,将他们转换成 Observables。一旦转换出一个新的 Observable,就只发出它的元素,旧的 Observables 的元素将被忽略掉。

  • flatMapLatestflatMap 的唯一区别是:flatMapLatest 只会接收最新的 value 事件。
  • flatMapLatest实际上是mapswitchLatest操作符的组合
  • flatMapLatest就会有flatMapFirstflatMapFirstflatMapLatest正好相反:flatMapFirst 只会接收最初的 value 事件。(该操作符可以防止重复请求:
    比如点击一个按钮发送一个请求,当该请求完成前,该按钮点击都不应该继续发送请求。便可该使用 flatMapFirst 操作符。)

例子:

    let disposeBag = DisposeBag()
    let Henry = BehaviorSubject(value: 1)
    let Jeannie = BehaviorSubject(value: 2)
    let Henry_Subject = BehaviorSubject(value: Henry)
        
    Henry_Subject.asObservable()
        .flatMapLatest{ $0 }
        .subscribe(onNext: { print($0) })
        .disposed(by: disposeBag)
        
    Henry.onNext(3)
    Henry_Subject.onNext(Jeannie)
    Jeannie.onNext(5)
    Henry.onNext(6)
    
    
    //结果:
    1
    3
    2
    5

4. concatMap

concatMap操作符会将 Observable的元素转换成其他的 Observable,然后将这些 Observables串连起来


concatMap操作符将源 Observable 的每一个元素应用一个转换方法,将他们转换成 Observables。然后让这些Observables 按顺序的发出元素.

  • concatMapflatMap 的唯一区别是:当前一个 Observable 元素发送完毕后,后一个 Observable 才可以开始发出元素。等待前一个Observable 产生完成事件后,才对后一个 Observable 进行订阅。

例子:

    let disposeBag = DisposeBag()
    let Henry = BehaviorSubject(value: 1)
    let Jeannie = BehaviorSubject(value: 2)
    let Henry_Subject = BehaviorSubject(value: Henry)
        
    Henry_Subject.asObservable()
        .concatMap{ $0 }
        .subscribe(onNext: { print($0) })
        .disposed(by: disposeBag)
        
    Henry.onNext(3)
    Henry_Subject.onNext(Jeannie)
    Jeannie.onNext(5)
    Jeannie.onNext(6)
    Henry.onCompleted()
    
    //结果:
    1
    3
    6

5. scan

scan操作符会持续的将Observable ,然后发出每一次函数返回的结果


scan 操作符将对第一个元素应用一个函数,将结果作为第一个元素发出。然后,将结果作为参数填入到第二个元素的应用函数中,创建第二个元素。以此类推,直到遍历完全部的元素。也就是scan 先给一个初始化的数,然后不断的拿前一个结果和最新的值进行处理操作。

例子:

    let disposeBag = DisposeBag()

    Observable.of(1, 2, 3, 4, 5)
        .scan(0) { acum, elem in
        acum + elem
    }
        .subscribe(onNext: { print($0) })
        .disposed(by: disposeBag)

    //结果
    1
    3
    6
    10
    15

6. reduce

reduce操作符和scan操作符还是有点类似的,reduce 操作符将对第一个元素应用一个函数。然后,将结果作为参数填入到第二个元素的应用函数中。以此类推,直到遍历完全部的元素后发出最终结果。


例子:

    let disposeBag = DisposeBag()

    Observable.of(10, 20, 30)
        .reduce(0, accumulator: +)
        .subscribe(onNext: { print($0) })
        .disposed(by: disposeBag)
        
    //结果:
    60

7.toArray

toArray操作符先把一个序列转成一个数组,并作为一个单一的事件发送,然后结束。


例子:

    let disposeBag = DisposeBag()
 
    Observable.of(1, 2, 3)
        .toArray()
        .subscribe(onNext: { print($0) })
        .disposed(by: disposeBag)
    //结果:
    [1,2,3]

组合操作符

1. merge

merge操作符会将多个Observables 合并成一个Observable.
通过使用 merge 操作符可以将多个 Observables 合并成一个,当某一个 Observable 发出一个元素时,它就将这个元素发出。

image

如果,其中某一个 Observable 发出一个 onError 事件,那么被合并的 这个Observable 也会将这个onError 事件发出,并且立即终止序列。

值得注意的是这里被合并的序列元素必须是同类型的。

例子:

    let disposeBag = DisposeBag()
        
    let Henry = PublishSubject<Int>()
    let Jeannie = PublishSubject<Int>()
        
    Observable.of(Henry, Jeannie)
        .merge()
        .subscribe(onNext: { print($0) })
        .disposed(by: disposeBag)
        
    Henry.onNext(1)
    Henry.onNext(2)
    Jeannie.onNext(100)
    Henry.onNext(3)
    Henry.onNext(4)
    Jeannie.onNext(1000)
    
    //结果:
    1
    2
    100
    3
    4
    1000
    

2. startWith

startWith操作符会在 Observable 头部插入一些事件元素。即发出Observable的事件元素之前,会先发出这些预先插入的事件元素。


如果在Observable头部分多次插入事件元素,那么这些事件元素会一直在Observable头部追加,也就是后插入的先发出来。

例子:

    let disposeBag = DisposeBag()
        
    Observable.of("1", "2")
        .startWith("A")
        .startWith("B")
        .startWith("a", "b")
        .subscribe(onNext: { print($0) })
        .disposed(by: disposeBag)
    
    //结果:
    a
    b
    B
    A
    1
    2

3. zip

zip操作符可将多个 Observables 的元素组合压缩,而且它会等到每个Observable 元素事件一一对应地凑齐之后再合并,然后将每一个组合的结果元素发出来。


zip操作符最多可以将8个Observables 的元素通过一个函数组合起来,然后将这个组合的结果发出来。它会严格的按照序列的索引数进行组合。例如,返回的 Observable的第一个元素,是由每一个源 Observables 的第一个元素组合出来的。它的第二个元素 ,是由每一个源 Observables 的第二个元素组合出来的。它的第三个元素 ,是由每一个源 Observables 的第三个元素组合出来的,以此类推。它的元素数量等于源 Observables中元素数量最少的那个。

例子:

    let disposeBag = DisposeBag()
    let Henry = PublishSubject<String>()
    let Jeannie = PublishSubject<String>()
        
    Observable.zip(Henry, Jeannie) { $0 + $1 }
        .subscribe(onNext: { print($0) })
        .disposed(by: disposeBag)
    
    Henry.onNext("1")
    Jeannie.onNext("A")
    Henry.onNext("2")
    Jeannie.onNext("B")
    Jeannie.onNext("C")
    Henry.onNext("3")
    Henry.onNext("4")
    
    //结果:
    1A
    2B
    3C

zip操作符常常用在整合网络请求上。
比如我们想同时发送两个请求,只有当两个请求都成功后,再将两者的结果整合起来继续往下处理。这个功能就可以通过 zip 来实现。

4. combineLatest

combineLatest操作符同样是将多个Observables 序列元素进行合并。但与zip不同的是,每当任意一个 Observable有新的事件发出时,它会将每个Observable 序列的最新的一个事件元素进行合并,然后发送这个组合出来的元素。(前提是,这些 Observables 曾经发出过元素)。

例子:

    let disposeBag = DisposeBag()
    let Henry = PublishSubject<String>()
    let Jeannie = PublishSubject<String>()
    
    Observable.combineLatest(Henry, Jeannie) { $0 + $1 }
        .subscribe(onNext: { print($0) })
        .disposed(by: disposeBag)
    
    Henry.onNext("1")
    Jeannie.onNext("A")
    Henry.onNext("2")
    Jeannie.onNext("B")
    Jeannie.onNext("C")
    Henry.onNext("3")
    Henry.onNext("4")
    
    //结果:
    1A
    2A
    2B
    2C
    3C
    4C

5. concat

concat 操作符将多个 Observables 按顺序串联起来,并且只有当前面一个 Observable 序列发出了 completed 事件,才会开始发送下一个 Observable序列事件。


例子:

    let disposeBag = DisposeBag()
    let Henry = BehaviorSubject(value: 1)
    let Jeannie = BehaviorSubject(value: 2)
    let Henry_Subject = BehaviorSubject(value: Henry)
        
    Henry_Subject.asObservable()
        .concat()
        .subscribe(onNext: { print($0) })
        .disposed(by: disposeBag)
        
    Henry.onNext(3)
    Henry_Subject.onNext(Jeannie)
    Jeannie.onNext(5)
    Jeannie.onNext(6)
    Henry.onCompleted()
    Jeannie.onNext(7)
    
    //结果:
    1
    3
    6
    7

感觉concat操作符和concatMap操作符比较相似,它们都是要等到前面一个 Observable 序列发出了 completed 事件,才会开始发送下一个 Observable序列事件。

6. withLatestFrom

withLatestFrom操作符会将两个 Observables 最新的元素通过一个函数组合起来,当第一个 Observable 发出一个元素,就将组合后的元素发送出来。


例子:

    let disposeBag = DisposeBag()
    let firstSubject = PublishSubject<String>()
    let secondSubject = PublishSubject<String>()

    firstSubject
         .withLatestFrom(secondSubject) {
              (first, second) in
              return first + second
         }
         .subscribe(onNext: { print($0) })
         .disposed(by: disposeBag)

    firstSubject.onNext("A️")
    secondSubject.onNext("1")
    firstSubject.onNext("B")
    
    //结果:
    B1

过滤条件操作符

1. filter

filter操作符就是用来过滤掉某些不符合要求的事件,仅仅发出Observable中通过判定的元素。

例子:

    let disposeBag = DisposeBag()

    Observable.of(11, 22, 3, 8, 2, 1)
          .filter { $0 > 10 }
          .subscribe(onNext: { print($0) })
          .disposed(by: disposeBag)
    
    //结果:
    11
    22

2. take

take操作符仅仅从 Observable中发出头n 个元素,在满足数量之后会自动发送 .completed事件。

例子:

    let disposeBag = DisposeBag()

    Observable.of(1, 2, 3, 4, 5, 6)
        .take(3)
        .subscribe(onNext: { print($0) })
        .disposed(by: disposeBag)
    
    //结果:
    1
    2
    3

3. takeLast

takeLast操作符与take操作符类似,实现仅发送 Observable 序列中的后n个元素,忽略前面的元素。

例子:

let disposeBag = DisposeBag()

    Observable.of(1, 2, 3, 4, 5, 6)
        .takeLast(3)
        .subscribe(onNext: { print($0) })
        .disposed(by: disposeBag)
    
    //结果:
    4
    5
    6

4. takeWhile

takeWhile操作符会依次判断 Observable 序列的每一个值是否满足给定的条件。 当第一个不满足条件的值出现时,它便自动完成。


例子:

   let disposeBag = DisposeBag()
 
   Observable.of(2, 3, 4, 5, 6)
       .takeWhile { $0 < 4 }
       .subscribe(onNext: { print($0) })
       .disposed(by: disposeBag)
       
    //结果:
    2
    3

5. takeUntil

takeUntil操作符会忽略掉在第二个Observable 产生事件后发出的那部分元素。也就是,takeUntil 操作符会观测源Observable,它同时观测第二个 Observable。一旦第二个 Observable 发出一个元素或者产生一个终止事件,那么源Observable 将自动完成,停止发送事件。


例子:

    let disposeBag = DisposeBag()
        
    let Henry = PublishSubject<String>()
    let Jeannie = PublishSubject<String>()
        
    Henry
        .takeUntil(Jeannie)
        .subscribe { print($0) }
        .disposed(by: disposeBag)
        
    Henry.onNext("Good")
    Henry.onNext("Lucky")
    Jeannie.onNext("Bug")
    Henry.onNext("Tnanks")
    Henry.onNext("a lot")
    
    //结果:
    next(Good)
    next(Lucky)
    completed

6. skip

skip操作符用于跳过源 Observable序列发出的前n 个元素事件。


例子:

    let disposeBag = DisposeBag()
 
    Observable.of(1, 2, 3, 4)
        .skip(2)
        .subscribe(onNext: { print($0) })
        .disposed(by: disposeBag)
        
    //结果:
    3
    4

7. skipWhile

skipWhile 操作符可以让你忽略源 Observable 中头几个满足条件的事件。

例子:

    let disposeBag = DisposeBag()
 
    Observable.of(2, 3, 4, 5, 6)
        .skipWhile { $0 < 5 }
        .subscribe(onNext: { print($0) })
         .disposed(by: disposeBag)
   
   //结果:
   5
   6

8. skipUntil

同上面的 takeUntil类似,skipUntil 除了订阅源 Observable外,通过 skipUntil 方法我们还可以监视另外一个 ObservableskipUntil操作符会跳过Observable 中头几个元素,直到另一个 Observable发出一个元素。


例子:

    let disposeBag = DisposeBag()
        
    let Henry = PublishSubject<Int>()
    let Jeannie = PublishSubject<Int>()
        
    Henry
        .skipUntil(Jeannie)
        .subscribe(onNext: { print($0) })
        .disposed(by: disposeBag)
        
    Henry.onNext(1)
    Henry.onNext(2)
    //开始接收消息
    Jeannie.onNext(0)
    Henry.onNext(3)
    Henry.onNext(4)
    //仍然接收消息
    Jeannie.onNext(0)
    Henry.onNext(5)
    
    //结果:
    3
    4
    5

9. elementAt

elementAt操作符只发出 Observable 中的第 n 个元素,即是只处理指定位置的元素事件


例子:

    let disposeBag = DisposeBag()

    Observable.of(1, 2, 3, 4)
        .elementAt(2)
        .subscribe(onNext: { print($0) })
        .disposed(by: disposeBag)
        
    //结果:
    3

10. distinctUntilChanged

distinctUntilChanged操作符用于过滤掉连续重复的事件。如果后一个元素和前一个元素是相同的,那么这个元素将不会被发出来。如果后一个元素和前一个元素不相同,那么这个元素才会被发出来。

例子:

    let disposeBag = DisposeBag()
 
    Observable.of(1, 2, 1, 1, 1, 3)
        .distinctUntilChanged()
        .subscribe(onNext: { print($0) })
        .disposed(by: disposeBag)
    
    //结果
    1
    2
    1
    3

11.amb

amb操作符在多个源 Observables中, 取第一个发出元素或产生事件的 Observable,这个事件可以是一个 nexterror 或者 completed事件,然后就只发出这个Observable的元素事件,忽略掉其他的Observables


例子:

    let disposeBag = DisposeBag()
 
    let subject1 = PublishSubject<Int>()
    let subject2 = PublishSubject<Int>()
    let subject3 = PublishSubject<Int>()
 
    subject1
        .amb(subject2)
        .amb(subject3)
        .subscribe(onNext: { print($0) })
        .disposed(by: disposeBag)
 
    subject2.onNext(1)
    subject1.onNext(20)
    subject2.onNext(2)
    subject1.onNext(40)
    subject3.onNext(0)
    subject2.onNext(3)
    subject1.onNext(60)
    subject3.onNext(0)
    subject3.onNext(0)
    
    //结果:
    1
    2
    3

总结

关于RxSwift的高阶函数先总结这么一部分。其实通过这些简单的示例来看,这些操作符在我们对序列进行操作的时候会有很大的帮助,而且使用简单。学好RxSwift,走遍天下都不怕。

感谢RxSwift中文文档

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,706评论 4 366
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,002评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,462评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,375评论 0 216
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,763评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,849评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,033评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,768评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,490评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,734评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,204评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,566评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,227评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,137评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,934评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,926评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,774评论 2 274