While Loop In Project Reactor? Expand as a Solution.

In this article, I will show you how to implement an equivalent of a while loop in Project Reactor using the expand() method.
This featured image for the post titled While Loop In Project Reactor? Expand as a Solution shows people looking from the hills.

1. Introduction

If you’ve ever been working with Project Reactor and have a need to implement an equivalent of a while (or do-while) loop, then you’ve come to the right place 🙂

In this article, I will show you what exactly the expand() method does in Project Reactor and how it can help you to solve the problem of a while (or rather do-while) loop.

2. Project Reactor Expand in Theory

As the first step, let’s try to understand what exactly the Mono.expand() method do? According to the documentation:

Recursively expand elements into a graph and emit all the resulting element using a breadth-first traversal strategy.

I know, it might sound a bit confusing, but let’s look at the following dependency graph:

The image shows a graph used to better ilustrate Mono.defer() method definition.

As we can see, it presents a hierarchical structure of the parent-child relationships. If we would expand a Mono.just(A), then the values would be emitted in the following order: A, B, C, D, E, F, G, H.

This behavior can be used to implement a counterpart of a while loop in Project Reactor.

3. Set Up Example Spring WebFlux Project

With that being done, let’s implement a simple Spring WebFlux project, which will help us to understand everything even better.

We will expose a simple REST endpoint with pagination and use the expand() method to fetch all the results.

Make a real progress thanks to practical examples, exercises, and quizzes.

Image presents a Kotlin Course box mockup for "Kotlin Handbook. Learn Through Practice"

3.1. Imports

As the first step, let’s add the necessary import to the project:

dependencies {
  implementation("org.springframework.boot:spring-boot-starter-webflux")
}

3.2. Expose The Test Endpoint

Nextly, let’s expose the example endpoint:

@RestController
@RequestMapping("/api")
class ExampleController(
  private val service: ExampleService
) {

  data class ExampleDto(val name: String, val nextPageToken: String?)

  val exampleList = mapOf(
    "" to ExampleDto("Name-1", "token-2"),
    "token-2" to ExampleDto("Name-2", "token-3"),
    "token-3" to ExampleDto("Name-3", "token-4"),
    "token-4" to ExampleDto("Name-4", "token-5"),
    "token-5" to ExampleDto("Name-5", null)
  )

  @GetMapping("/example")
  fun getExample(@RequestParam("token") pageToken: String?): Mono<ExampleDto> =
    pageToken?.let { token ->
      Mono.just(exampleList[token]!!)
    } ?: Mono.empty()

}

As we can see, the logic is pretty straightforward.

When the API consumer performs a GET request with an empty String, the endpoint returns the name and nextPageToken properties:

{
    "name": "Name-1",
    "nextPageToken": "token-2"
}

The nextPageToken property can be then used to iterate through the entries in the Map. Finally, when the user gets the last record, the nextPageToken property is null and the following request results in the empty Mono being returned.

as a note, I can add that the Google Calendar API works in a similar manner 🙂

4. Project Reactor While Loop

So at this point, we already have our REST endpoint and it’s time to consume it with a do-while loop in some way.

4.1. Add WebClient Config

As the first step, let’s add the WebClient config:

@Configuration
class Configuration {

  @Bean
  fun webClient(): WebClient =
    WebClient.builder()
      .baseUrl("http://localhost:8080")
      .build()

}

4.2. Create Example Service

Nextly let’s implement the ExampleService and a performRequest function:

@Service
class ExampleService(
    private val webClient: WebClient
) {
  
  private fun performRequest(token: String?): Mono<ExampleController.ExampleDto> =
    webClient
      .get()
      .uri { builder ->
        builder
          .path("/api/example")
          .queryParamIfPresent("token", Optional.ofNullable(token))
          .build()
      }
      .retrieve()
      .bodyToMono(ExampleController.ExampleDto::class.java)
}

As can be seen, this function is responsible for performing a GET request to the http://localhost:8080/api/example endpoint. The token query parameter is set based on the passed argument (which can be null, as well).

4.3. Make Use Of Mono.expand() Method

Finally, let’s make use of the Project Reactor Mono.expand() method to iterate through the results in a do-while style:

operator fun invoke() {
  performRequest("")
    .expand { response ->
      performRequest(token = response.nextPageToken)
    }
    .subscribe(System.out::println)
}

Now, when we invoke the above function, the following values will be printed to the output:

ExampleDto(name=Name-1, nextPageToken=token-2)
ExampleDto(name=Name-2, nextPageToken=token-3)
ExampleDto(name=Name-3, nextPageToken=token-4)
ExampleDto(name=Name-4, nextPageToken=token-5)
ExampleDto(name=Name-5, nextPageToken=null)

As we can see, the initial Mono has been expanded and the values are published until the endpoint returns the empty Mono.

5. Summary

And that would be all for this article on how to implement a while (do-while) loop in Project Reactor.

As always, you can find the source code right here, in this GitHub repository, and the other articles related to Project Reactor here.

If you would like to add anything, or just simply share your thoughts, then please let me know in the comment section. Have a great week and see you in the next articles 🙂

Share this:

Related content

Newsletter
Image presents 3 ebooks with Java, Spring and Kotlin interview questions.

Never miss any important updates from the Kotlin world and get 3 ebooks!

You may opt out any time. Terms of Use and Privacy Policy