Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
98 changes: 98 additions & 0 deletions documentation/docs-roq/content/2.0.0/guides/collecting-items.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
---
title: "Collecting items from Multi"
layout: page
tags:
- guide
- beginner
---

# Collecting items from Multi

There are cases where you want to accumulate the items from a `Multi` and produce a `Uni` as a final result.
This is also called a _reduction_ in functional programming.

The `Uni` fires its item when the `Multi` completes.
Mutiny provides multiple operators to deal with that scenario.
They are available from the `collect()` group.
For example, you can store the items in a list, emit the list on completion, or use a Java `Collector` to customize the aggregation.

> [!CAUTION]
>
> Don't collect items from infinite streams or you will likely end with an out-of-memory failure!

## Collecting items into a list

One of the most common approaches to collect items is to store them in a list (`Uni<List<T>>`)
It emits the final list when the `Multi` completes.

```mermaid
sequenceDiagram
autonumber
participant M as Multi
participant O as Collect operator
participant D as Subscriber

M->>O: onItem(1)
M->>O: onItem(2)
M->>O: onItem(3)

O->>D: onItem([1, 2, 3])
```

How to achieve this with Mutiny?

```java linenums="1"
{=snippet:insert("java/guides/operators/CollectingItemsTest.java", "list")}
```

It's important to note that the returned type is a `Uni`.
It emits the list when the multi completes.

## Collecting items into a map

You can also collect the items into a `Map`.
In this case, you need to provide a function to compute the key for each item:

```java linenums="1"
{=snippet:insert("java/guides/operators/CollectingItemsTest.java", "map")}
```

If the key mapper function returns the same key for multiple items, the last one with that key is stored in the final `Map`.
You can collect items in a _multimap_ to handle items with the same keys.

## Collecting items into a multimap

A multimap is a `Map<K, List<T>>.`
In the case of a conflicting key, it stores all the items in a list associated with that key.

```java linenums="1"
{=snippet:insert("java/guides/operators/CollectingItemsTest.java", "multimap")}
```

## Using a custom accumulator

You can also use a custom _accumulator_ function:

```java linenums="1"
{=snippet:insert("java/guides/operators/CollectingItemsTest.java", "accumulator")}
```

The `in` method receives two arguments:

1. a supplier providing the new instance of your collection/container
2. the accumulator function

You can also use a Java `Collector`.
For example, in the next example, count the number of items, and produce the final count as item:

```java linenums="1"
{=snippet:insert("java/guides/operators/CollectingItemsTest.java", "collector")}
```

## Getting the first and last items

While they are not strictly speaking collecting items, `collect().first()` and `collect().last()` allow retrieving the first and last item from a `Multi`:

```java linenums="1"
{=snippet:insert("java/guides/operators/CollectingItemsTest.java", "first")}
```
159 changes: 159 additions & 0 deletions documentation/docs-roq/content/2.0.0/guides/combining-items.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
---
title: "Combining items from streams"
layout: page
tags:
- guide
- intermediate
---

# Combining items from streams

Combining items from various streams is an essential pattern in Reactive Programming.

It associates the emitted items from multiple streams and emits an _aggregate_.
The downstream receives this _aggregate_ and can handle it smoothly.

There are plenty of use cases, such as executing two tasks concurrently and waiting for both completions, getting the last items from different streams to build an always up-to-date view, and so on.

## Combining Unis

Imagine that you have two asynchronous operations to perform like 2 HTTP requests.
You want to send these requests and be notified when both have completed with their responses ready to be consumed.

Of course, you could send the first request, wait for the response, and then send the second request.
If both requests are independent, we can do something better: send both concurrently and await for both completions!

```mermaid
sequenceDiagram
autonumber
participant A as Stream A
participant B as Stream B
participant M as Combined stream
participant S as Subscriber

A->>M: onItem(1)
B->>M: onItem(a)

M->>S: onItem([1,a])

A->>M: onItem(2)
B->>M: onItem(b)

M->>S: onItem([2,b])
```

How can you achieve this with Mutiny?

First, each request is a `Uni`, so we have:

```java linenums="1"
{=snippet:insert("java/guides/operators/CombiningItemsTest.java", "invocations")}
```

Then, we want to combine both _responses_:

```java linenums="1"
{=snippet:insert("java/guides/operators/CombiningItemsTest.java", "combination")}
```

This code creates a new `Uni` produced by combining `uniA` and `uniB`.
The responses are aggregated inside a `Tuple`:

```java linenums="1"
{=snippet:insert("java/guides/operators/CombiningItemsTest.java", "subscription")}
```

The `tuple` aggregates the responses in the same order as the `Uni` sequence.

If one of the `Uni` fails, so does the combination and you receive the failure:

```java linenums="1"
{=snippet:insert("java/guides/operators/CombiningItemsTest.java", "invocations")}
{=snippet:insert("java/guides/operators/CombiningItemsTest.java", "combination")}
{=snippet:insert("java/guides/operators/CombiningItemsTest.java", "subscription")}
```

Using tuples is convenient but only works if you have less than 10 `Uni` objects.
If you want another structure or deal with 10 `Uni` objects or more then use `combineWith`:

```java linenums="1"
{=snippet:insert("java/guides/operators/CombiningItemsTest.java", "combined-with")}
```

## Combining Multis

Combining `Multis` consists of associating items from different stream per _index_:

```mermaid
sequenceDiagram
autonumber
participant A as Stream A
participant B as Stream B
participant M as Combined stream
participant S as Subscriber

A->>M: onItem(1)
A->>M: onItem(2)
B->>M: onItem(a)

M->>S: onItem([1,a])

A->>M: onItem(3)
B->>M: onItem(b)
M->>S: onItem([2,b])

B->>M: onItem(c)
```

It associates the first items from the combined streams, then the second items:

```java linenums="1"
{=snippet:insert("java/guides/operators/CombiningItemsTest.java", "combine-multi")}
```

As for `Uni`, you can aggregate the item into tuples (up to 9 items) or combine with a combinator function:

```java linenums="1"
{=snippet:insert("java/guides/operators/CombiningItemsTest.java", "combine-multi-with")}
```

If one of the streams fails, the combined stream propagates the failure and stops the emission.
The combined stream completes as soon as one of the observed stream sends the completion event.

> [!NOTE]
>
> If one of the observed streams never emits any item then the combined stream will not emit anything.

## Combining the latest items of Multis

It can be useful to combine multiple `Multi` streams and receive the _latest_ items from each stream on every emission:


```mermaid
sequenceDiagram
autonumber
participant A as Stream A
participant B as Stream B
participant M as Combined stream
participant S as Subscriber

A->>M: onItem(1)
A->>M: onItem(2)
B->>M: onItem(a)

M->>S: onItem([2,a])

A->>M: onItem(3)
M->>S: onItem([3,a])
B->>M: onItem(b)
M->>S: onItem([3,b])

B->>M: onItem(c)
M->>S: onItem([3,c])
```

This is achieved using `latest()`:

```java linenums="1"
{=snippet:insert("java/guides/operators/CombiningItemsTest.java", "combine-last")}
```
84 changes: 84 additions & 0 deletions documentation/docs-roq/content/2.0.0/guides/completion-stage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
title: "How to deal with CompletionStage?"
layout: page
tags:
- guide
- intermediate
---

# How to deal with CompletionStage?

`CompletionStage` and `CompletableFuture` are classes provided by Java to represent asynchronous actions.

## Differences between Uni and CompletionStage

While `CompletionStage` and `CompletableFuture` are close to `Uni` in terms of use case, there are some fundamental differences.

`CompletionStage` are _eager_.
When a method returns a `CompletionStage,` the operation has already been triggered.
The outcome is used to complete the returned `CompletionStage`.
On the other side, `Unis` are lazy.
The operation is only triggered once there is a subscription.

`CompletionStage` _caches_ the outcome.
So, once received, you can retrieve the result.
Every retrieval will get the same result.
With `Uni`, every subscription has the opportunity to re-trigger the operation and gets a different result.

> [!TIP]
>
> You can also _cache_ the outcome with `Uni.memoize().indefinitely()`.

## From Uni to CompletionStage

You can create a `CompletionStage` from `Uni` using `uni.subscribeAsCompletionStage()`.

```java linenums="1"
{=snippet:insert("java/guides/CompletionStageTest.java", "uni-subscribe-cs")}
```

It's important to understand that retrieving a `CompletionStage` subscribes to the `Uni`.
If you do this operation twice, it subscribes to the `Uni` twice and re-trigger the operation.

```java linenums="1"
{=snippet:insert("java/guides/CompletionStageTest.java", "uni-subscribe-cs-twice")}
```

## Creating a Uni from a CompletionStage

To create a `Uni` from a `CompletionStage`, use `Uni.createFrom().completionStage(...)`.

```java linenums="1"
{=snippet:insert("java/guides/CompletionStageTest.java", "create-uni")}
```

As you can see, there are two versions.
The first one receives the `CompletionStage` directly, while the second one gets a supplier.
In the case of multiple subscriptions on the produced `Uni`, the supplier is called multiple times (once per subscription), and so can change the return `CompletionStage`.
It also delays the creation of the `CompletionStage` until there is a subscription, which only triggers the operation at that time.
If you pass the instance directly, it will always use the same one (even for multiple subscriptions) and triggers the operation even if there is no subscription.
For these reasons, it is generally better to use the variant accepting a supplier.

Note that if the completion stage produces a `null` value, the resulting `Uni` emits `null` as item.
If the completion stages complete exceptionally, the failure is emitted by the resulting `Uni`.

## Creating a Multi from a CompletionStage

To create a `Multi` from a `CompletionStage`, use `Multi.createFrom().completionStage(...)`.
It produces:

* a multi emitting an item and completing - if the value produced by the completion stage is not `null`,
* an empty multi if the value produced by the completion stage is `null`,
* a failed multi is completion stage is completed exceptionally.

```java linenums="1"
{=snippet:insert("java/guides/CompletionStageTest.java", "create-multi")}
```

For the same reason as for `Uni`, there are two versions:

1. one accepting a `CompletionStage` directly
2. one accepting a `Supplier<CompletionStage>`, called at subscription-time, for every subscription.

It is recommended to use the second version.

Loading