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

1,409 阅读6分钟

前言

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 发出一个元素时,它就将这个元素发出。

如果,其中某一个 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中文文档