响应式编程:响应式流操作符学习|青训营

94 阅读3分钟

操作符

1、创建:

  • just()
Flux<String> fruitFlux = Flux.just("Apple", "Orange", "Grape", "Banana", "Strawberry");
  • 基于数组、集合、stream
Integer[] array = new Integer[]{1,2,3,4,5,6};
Flux.fromArray(array);
List<Integer> list = Arrays.asList(array);
Flux.fromIterable(list);
Stream<Integer> stream = list.stream();
Flux.fromStream(stream);
  • range(n,m):创建起始值为n,每个元素递增1,总共m个元素

    img

    Flux.range(1, 5); // 1,2,3,4,5
    
  • interval():每隔相应时间发送一次

2、订阅:subscribe()

订阅前什么都不会发生:只有订阅方法调用的时候,才会触发数据流

fruitFlux.subscribe(
    f -> System.out.println("Here's some fruit: " + f);
);

3、测试:StepVerifier订阅响应式类型,然后对流中流动的数据应用断言,最后验证流以预期方式完成。

StepVerifier.create(fruitFlux)
    .expectNext("Apple")
    .expectNext("Orange")
    .expectNext("Grape")
    .expectNext("Banana")
    .expectNext("Strawberry")
    .verifyComplete();

expectNext用于测试下一个期望的数据元素,expectErrorMessage用于校验下一个元素是否为错误信号,expectComplete用于测试下一个元素是否为完成信号。

4、合并:mergeWith()

@Test
public void mergeFluxes() {
    Flux<String> characterFlux = Flux
        .just("Garfield", "Kojak", "Barbossa")
        .delayElements(Duration.ofMillis(500)); //减慢发送速度,每0.5秒发送一个
​
    Flux<String> foodFlux = Flux
        .just("Lasagna", "Lollipops", "Apples")
        .delaySubscription(Duration.ofMillis(250))//延迟250ms之后再发送数据
        .delayElements(Duration.ofMillis(500));
​
    Flux<String> mergedFlux = characterFlux.mergeWith(foodFlux);
​
    StepVerifier.create(mergedFlux)
        .expectNext("Garfield")
        .expectNext("Lasagna")
        .expectNext("Kojak")
        .expectNext("Lollipops")
        .expectNext("Barbossa")
        .expectNext("Apples")
        .verifyComplete(); //合并顺序和发送顺序一致
}

img

5、压缩:zip():对两个Flux流每次各取一个元素,合并为一个二元组(Tuple2),也可以指定function接口

img

6、过滤

  • skip(n):跳过前n项数据
  • skip(Duration.ofSeconds(n)):发出任何值之前等待n秒?
  • take(n):只发出前n项数据
  • take(Duration.ofMillis(n))
  • distinct():去重
  • filter():自定义过滤规则,满足规则的留下
@Test
public void filter() {
    Flux<String> nationalParkFlux = Flux.just(
        "Yellowstone", "Yosemite", "Grand Canyon","Zion", "Grand Teton")
        .filter(np -> !np.contains(" "));//不含有空格的留下
​
    StepVerifier.create(nationalParkFlux)
        .expectNext("Yellowstone", "Yosemite", "Zion")
        .verifyComplete();
}

7、映射

  • map()
@Test
public void map() {
    Flux<Player> playerFlux = Flux
        .just("Michael Jordan", "Scottie Pippen", "Steve Kerr")
        .map(n -> {
            String[] split = n.split("\s");  //将名字的名和姓分开
            return new Player(split[0], split[1]); //映射为有名有姓的运动员
        });
​
    StepVerifier.create(playerFlux)
        .expectNext(new Player("Michael", "Jordan"))
        .expectNext(new Player("Scottie", "Pippen"))
        .expectNext(new Player("Steve", "Kerr"))
        .verifyComplete();
}
  • flatmap():异步。将每个元素映射为流,再将这些流合并为大的数据流
@Test
public void flatMap() {
    Flux<Player> playerFlux = Flux
        .just("Michael Jordan", "Scottie Pippen", "Steve Kerr")
        .flatMap(n -> Mono.just(n).map(p -> {
            String[] split = p.split("\s");
            return new Player(split[0], split[1]); //将一个元素映射为只有一个数据的流
        })
        .subscribeOn(Schedulers.parallel()) //指示每个订阅应该在一个并行线程中
        );
​
    List<Player> playerList = Arrays.asList(
        new Player("Michael", "Jordan"),
        new Player("Scottie", "Pippen"),
        new Player("Steve", "Kerr"));
​
    StepVerifier.create(playerFlux)
        .expectNextMatches(p -> playerList.contains(p))
        .expectNextMatches(p -> playerList.contains(p))
        .expectNextMatches(p -> playerList.contains(p))
        .verifyComplete();
}

subscribeOn() :指定了应该如何并发地处理订阅。可以使用 Schedulers 程序中的一个静态方法指定要使用的并发模型。调度程序支持多个并发模型:

使用 flatMap() 和 subscribeOn() 的好处是,可以通过将工作分成多个并行线程来增加流的吞吐量。但由于这项工作是并行完成的,无法保证先完成哪项工作,因此无法知道产生的 Flux 中排放的项目的顺序。因此,StepVerifier 只能验证发出的每个项是否存在于 Player 对象的预期列表中,并且在 Flux 完成之前将有三个这样的项。

8、缓冲:buffer() 将数据流分解成指定大小的数据块

@Test
public void buffer() {
    Flux<String> fruitFlux = Flux.just(
        "apple", "orange", "banana", "kiwi", "strawberry");
​
    Flux<List<String>> bufferedFlux = fruitFlux.buffer(3);
​
    StepVerifier
        .create(bufferedFlux)
        .expectNext(Arrays.asList("apple", "orange", "banana"))
        .expectNext(Arrays.asList("kiwi", "strawberry"))
        .verifyComplete();
}

将buffer() 与 flatMap() 结合使用时,它可以并行处理每个 List 集合:

Flux.just("apple", "orange", "banana", "kiwi", "strawberry")
    .buffer(3)
    .flatMap(x -> 
         Flux.fromIterable(x)
             .map(y -> y.toUpperCase())
             .subscribeOn(Schedulers.parallel())
             .log()  //日志
    ).subscribe();

9、逻辑

  • any():任意一个满足条件
  • all():全部都满足条件