使用常見的反應式操作



import lombok.Data;
import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import reactor.test.StepVerifier;
import reactor.util.function.Tuple2;

import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.stream.Stream;

public class FluxTest {
    /**
     * just創建數據流
     */
    @Test
    public void createAFluxJust() {
        // 創建數據流,Flux裏邊數據結構是數組final T[] array
        Flux<String> fruitFlux = Flux.just("Apple", "Orange", "Grape", "Banana", "Strawberry");
        fruitFlux.subscribe(
                s -> System.out.println("Here is some fruit:" + s)
        );
        // StepVerifier作爲校驗數據流的方法
        StepVerifier.create(fruitFlux)
                .expectNext("Apple")
                .expectNext("Orange")
                .expectNext("Grape")
                .expectNext("Banana")
                .expectNext("Strawberry")
                .verifyComplete();
    }

    /**
     * Iterable創建數據流
     */
    @Test
    public void createAFluxFromIterable() {
        Flux<String> fruitFlux = Flux.fromIterable(Arrays.asList("Apple", "Orange", "Grape", "Banana", "Strawberry"));
        fruitFlux.subscribe(
                s -> System.out.println("Iterable創建數據流:" + s)
        );
        // StepVerifier作爲校驗數據流的方法
        StepVerifier.create(fruitFlux)
                .expectNext("Apple")
                .expectNext("Orange")
                .expectNext("Grape")
                .expectNext("Banana")
                .expectNext("Strawberry")
                .verifyComplete();
    }

    /**
     * Stream創建數據流
     */
    @Test
    public void createAFluxFromStream() {
        Flux<String> fruitFlux = Flux.fromStream(Stream.of("Apple", "Orange", "Grape", "Banana", "Strawberry"));
        fruitFlux.subscribe(
                s -> System.out.println("Stream創建數據流:" + s)
        );
        // StepVerifier作爲校驗數據流的方法
        StepVerifier.create(fruitFlux)
                .expectNext("Apple")
                .expectNext("Orange")
                .expectNext("Grape")
                .expectNext("Banana")
                .expectNext("Strawberry")
                .verifyComplete();
    }

    /**
     * range創建數據流
     */
    @Test
    public void createAFluxRange() {
        Flux<Integer> fruitFlux = Flux.range(1, 10);
        fruitFlux.subscribe(
                s -> System.out.println("range創建數據流:" + s)
        );
        // StepVerifier作爲校驗數據流的方法
        StepVerifier.create(fruitFlux)
                .expectNext(1)
                .expectNext(2)
                .expectNext(3)
                .expectNext(4)
                .expectNext(5)
                .verifyComplete();
    }

    /**
     * interval創建數據流
     */
    @Test
    public void createAFluxInterval() {
        Flux<Long> fruitFlux = Flux.interval(Duration.ofSeconds(1))
                // 限制最大值
                .take(5);
        fruitFlux.subscribe(
                s -> System.out.println("interval創建數據流:" + s)
        );
        // StepVerifier作爲校驗數據流的方法
        StepVerifier.create(fruitFlux)
                .expectNext(0L)
                .expectNext(1L)
                .expectNext(2L)
                .expectNext(3L)
                .expectNext(4L)
                .verifyComplete();
    }

    /**
     * 合併但不創建流,並不能保證順序
     */
    @Test
    public void mergeFluxes() {
        // 構建角色流
        Flux<String> characterFlux = Flux
                .just("Garfield", "Kojak", "Barbossa")
                // 減慢發佈速度
                .delayElements(Duration.ofMillis(100));
        // 構建食物流
        Flux<String> foodFlux = Flux
                .just("Lasagna", "Lollipops", "Apples", "Garbage")
                // 延緩發佈
                .delaySubscription(Duration.ofMillis(250))
                // 減慢發佈速度
                .delayElements(Duration.ofMillis(500));
        // merge
        Flux<String> mergeFlux = characterFlux.mergeWith(foodFlux);
        mergeFlux.subscribe(
                s -> System.out.println("mergeFluxes數據流:" + s)
        );
        // StepVerifier作爲校驗數據流的方法,由於merge不能保證順序,
        // 會報錯,所以使用delaySubscription推遲發佈
        StepVerifier.create(mergeFlux)
                .expectNext("Garfield")
                .expectNext("Kojak")
                .expectNext("Barbossa")
                .expectNext("Lasagna")
                .expectNext("Lollipops")
                .expectNext("Apples")
                .expectNext("Garbage")
                .verifyComplete();
    }

    /**
     * Flux靜態方法,交叉合併創建新的數據流
     * 不會輸出Garbage
     */
    @Test
    public void zipFluxes() {
        // 構建角色流
        Flux<String> characterFlux = Flux
                .just("Garfield", "Kojak", "Barbossa");
        // 構建食物流
        Flux<String> foodFlux = Flux
                .just("Lasagna", "Lollipops", "Apples", "Garbage");
        // merge
        Flux<Tuple2<String, String>> zipFlux = Flux.zip(characterFlux, foodFlux);
        zipFlux.subscribe(
                s -> System.out.println("zipFluxes交叉合併數據流:" + s.getT1() + "---" + s.getT2())
        );
    }

    /**
     * Flux靜態方法,交叉合併創建新的數據流對象,
     * 不會輸出Garbage
     */
    @Test
    public void zipFluxesToObject() {
        // 構建角色流
        Flux<String> characterFlux = Flux
                .just("Garfield", "Kojak", "Barbossa");
        // 構建食物流
        Flux<String> foodFlux = Flux
                .just("Lasagna", "Lollipops", "Apples", "Garbage");
        // merge
        Flux<String> zipFlux = Flux.zip(characterFlux, foodFlux, new BiFunction<String, String, String>() {
            @Override
            public String apply(String c, String f) {
                return c + " eats " + f;
            }
        });
        zipFlux.subscribe(
                s -> System.out.println("zipFluxes交叉合併數據流:" + s)
        );
    }

    /**
     * 選擇第一個反應式類型進行發佈
     */
    @Test
    public void firstFlux() {
        // delay needed to "slow down" the slow Flux

        Flux<String> slowFlux = Flux.just("tortoise", "snail", "sloth")
                .delaySubscription(Duration.ofMillis(100));
        Flux<String> fastFlux = Flux.just("hare", "cheetah", "squirrel");

        Flux<String> firstFlux = Flux.first(slowFlux, fastFlux);

        StepVerifier.create(firstFlux)
                .expectNext("hare")
                .expectNext("cheetah")
                .expectNext("squirrel")
                .verifyComplete();
    }

    /**
     * 從反應式類型中過濾數據
     */
    @Test
    public void skipAFew() {
        Flux<String> countFlux = Flux.just(
                "one", "two", "skip a few", "ninety nine", "one hundred")
                .skip(3);

        StepVerifier.create(countFlux)
                .expectNext("ninety nine", "one hundred")
                .verifyComplete();
    }

    /**
     * 創建了一個在發佈值之前會等待4秒的Flux
     */
    @Test
    public void skipAFewSeconds() {
        Flux<String> countFlux = Flux.just(
                "one", "two", "skip a few", "ninety nine", "one hundred")
                .delayElements(Duration.ofSeconds(1))
                .skip(Duration.ofSeconds(4));

        StepVerifier.create(countFlux)
                .expectNext("ninety nine", "one hundred")
                .verifyComplete();
    }

    /**
     * take操作只發布第一批指定數量的數據項
     */
    @Test
    public void take() {
        Flux<String> nationalParkFlux = Flux.just(
                "Yellowstone", "Yosemite", "Grand Canyon", "Zion", "Acadia")
                .take(3);

        StepVerifier.create(nationalParkFlux)
                .expectNext("Yellowstone", "Yosemite", "Grand Canyon")
                .verifyComplete();
    }

    /**
     * 訂閱之後的前3.5秒發佈數據條目
     */
    @Test
    public void takeForAwhile() {
        Flux<String> nationalParkFlux = Flux.just(
                "Yellowstone", "Yosemite", "Grand Canyon", "Zion", "Grand Teton")
                .delayElements(Duration.ofSeconds(1))
                .take(Duration.ofMillis(3500));

        StepVerifier.create(nationalParkFlux)
                .expectNext("Yellowstone", "Yosemite", "Grand Canyon")
                .verifyComplete();
    }

    @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();
    }

    @Test
    public void distinct() {
        Flux<String> animalFlux = Flux.just(
                "dog", "cat", "bird", "dog", "bird", "anteater")
                .distinct();

        StepVerifier.create(animalFlux)
                .expectNext("dog", "cat", "bird", "anteater")
                .verifyComplete();
    }

    @Test
    public void map() {
        Flux<FluxTest.Player> playerFlux = Flux
                .just("Michael Jordan", "Scottie Pippen", "Steve Kerr")
                .map(n -> {
                    String[] split = n.split("\\s");
                    return new FluxTest.Player(split[0], split[1]);
                });

        StepVerifier.create(playerFlux)
                .expectNext(new FluxTest.Player("Michael", "Jordan"))
                .expectNext(new FluxTest.Player("Scottie", "Pippen"))
                .expectNext(new FluxTest.Player("Steve", "Kerr"))
                .verifyComplete();
    }

    /**
     * flatMap並不像map操作那樣簡單地將一個對象轉換到另一個對象,
     * 而是將對象轉換爲新的Mono或Flux。結果形成的Mono或Flux會扁平化爲新的Flux。
     * 當與subscribeOn()方法結合使用時,flatMap操作可以釋放Reactor反應式的異步能力。
     * <p>
     * 使用flatMap()和subscribeOn()的好處是:我們可以在多個並行線程之間拆分工作,
     * 從而增加流的吞吐量。因爲工作是並行完成的,無法保證哪項工作首先完成,
     * 所以結果Flux中數據項的發佈順序是未知的。
     * Schedulers.parallel()是使用了
     * Executors.newSingleThreadScheduledExecutor()啓動線程,
     * 線程數量Runtime.getRuntime().availableProcessors()
     */
    @Test
    public void flatMap() {
        Flux<FluxTest.Player> playerFlux = Flux
                .just("Michael Jordan", "Scottie Pippen", "Steve Kerr")
                .flatMap(n -> Mono.just(n)
                        .map(p -> {
                            String[] split = p.split("\\s");
                            return new FluxTest.Player(split[0], split[1]);
                        })
                        // 它聲明每個訂閱都應該在並行線程中進行,
                        // 因此可以異步並行地執行多個String對象的轉換操作
                        .subscribeOn(Schedulers.parallel())
                );
        playerFlux.subscribe(
                s -> System.out.println("mergeFluxes數據流:" + s)
        );
        List<Player> playerList = Arrays.asList(
                new FluxTest.Player("Michael", "Jordan"),
                new FluxTest.Player("Scottie", "Pippen"),
                new FluxTest.Player("Steve", "Kerr"));

        StepVerifier.create(playerFlux)
                .expectNextMatches(p -> playerList.contains(p))
                .expectNextMatches(p -> playerList.contains(p))
                .expectNextMatches(p -> playerList.contains(p))
                .verifyComplete();
    }

    @Data
    private static class Player {
        private final String firstName;
        private final String lastName;
    }

    /**
     * 在反應式流上緩存數據
     */
    @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();
    }

    @Test
    public void bufferAndFlatMap() {
        Flux.just(
                "apple", "orange", "banana", "kiwi", "strawberry")
                .buffer(3)
                .flatMap(x ->
                        Flux.fromIterable(x)
                                .map(String::toUpperCase)
                                .subscribeOn(Schedulers.parallel())
                                .log()
                ).subscribe();
    }

    @Test
    public void collectList() {
        Flux<String> fruitFlux = Flux.just(
                "apple", "orange", "banana", "kiwi", "strawberry");

        Mono<List<String>> fruitListMono = fruitFlux.collectList();

        StepVerifier
                .create(fruitListMono)
                .expectNext(Arrays.asList(
                        "apple", "orange", "banana", "kiwi", "strawberry"))
                .verifyComplete();
    }

    @Test
    public void collectMap() {
        Flux<String> animalFlux = Flux.just(
                "aardvark", "elephant", "koala", "eagle", "kangaroo");

        Mono<Map<Character, String>> animalMapMono =
                animalFlux.collectMap(a -> a.charAt(0));

        StepVerifier
                .create(animalMapMono)
                .expectNextMatches(map -> {
                    return
                            map.size() == 3 &&
                                    map.get('a').equals("aardvark") &&
                                    map.get('e').equals("eagle") &&
                                    map.get('k').equals("kangaroo");
                })
                .verifyComplete();
    }

    @Test
    public void all() {
        Flux<String> animalFlux = Flux.just(
                "aardvark", "elephant", "koala", "eagle", "kangaroo");

        Mono<Boolean> hasAMono = animalFlux.all(a -> a.contains("a"));
        StepVerifier.create(hasAMono)
                .expectNext(true)
                .verifyComplete();

        Mono<Boolean> hasKMono = animalFlux.all(a -> a.contains("k"));
        StepVerifier.create(hasKMono)
                .expectNext(false)
                .verifyComplete();
    }

    @Test
    public void any() {
        Flux<String> animalFlux = Flux.just(
                "aardvark", "elephant", "koala", "eagle", "kangaroo");

        Mono<Boolean> hasAMono = animalFlux.any(a -> a.contains("a"));

        StepVerifier.create(hasAMono)
                .expectNext(true)
                .verifyComplete();

        Mono<Boolean> hasZMono = animalFlux.any(a -> a.contains("z"));
        StepVerifier.create(hasZMono)
                .expectNext(false)
                .verifyComplete();

    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章