Reactor-core: Provide a dedicated method on Mono which is similar to Optional.orElseThrow()

Created on 26 Oct 2017  路  8Comments  路  Source: reactor/reactor-core

Sorry if this exists already but I couldn't find it.

Expected behavior

An easy way to specify exceptions (lazily) in case the Mono is empty. Something that would allow to do this:

Mono.empty().errorIfEmpty(() -> new RuntimeException())

Currently we can do:

Mono.empty().switchIfEmpty(Mono.error(new RuntimeException()))
which is not lazy (the exception is always created)

Mono.empty().switchIfEmpty(Mono.defer(() -> Mono.error(new RuntimeException())))
which is lazy but verbose.

Alternatively it would be enough (and more general) if we had a "defer" variant for switchIfEmpty which would take a Supplier<Mono> similar to defer(). Then we could do:

Mono.empty().deferSwitchIfEmpty(() -> Mono.error(new RuntimeException()))

It is more verbose than a dedicated errorIfEmpty however.

Reactor Core version

3.1.0.RELEASE

statuhas-workaround

Most helpful comment

I'm not sure we want to add a dedicated operator for that, especially since there is an obvious (albeit verbose) workaround, as you have found.

It is a normal and desirable thing to rely on operator composition in a library like Reactor.

What I'd suggest instead, to remove some of the verbosity, is to put the switch/defer/error boilerplate into a method to use with transform:

public <T> Mono<T> errorIfEmpty(Mono<T> source) {
  return source.switchIfEmpty(Mono.defer(() -> Mono.error(new RuntimeException())));
}

Mono<String> foo = Mono.empty().transform(this::errorIfEmpty);

With the added advantage that it can be reused on other Monos.

All 8 comments

I'm not sure we want to add a dedicated operator for that, especially since there is an obvious (albeit verbose) workaround, as you have found.

It is a normal and desirable thing to rely on operator composition in a library like Reactor.

What I'd suggest instead, to remove some of the verbosity, is to put the switch/defer/error boilerplate into a method to use with transform:

public <T> Mono<T> errorIfEmpty(Mono<T> source) {
  return source.switchIfEmpty(Mono.defer(() -> Mono.error(new RuntimeException())));
}

Mono<String> foo = Mono.empty().transform(this::errorIfEmpty);

With the added advantage that it can be reused on other Monos.

That is understandable. I thought this a common enough use case to get a method but yes, definitely not a must have in the reactor library itself.

All righty, I'll close this issue for now then @akiraly, we'll consider it if it comes up again 馃槈

Just for completeness this is the variant I came up with:

  public <R> Mono<R> errorIfEmpty(Mono<R> mono, Supplier<Throwable> throwableSupplier) {
    requireNonNull(mono, "mono");
    requireNonNull(throwableSupplier, "throwableSupplier");
    return mono.switchIfEmpty(Mono.defer(() -> Mono.error(throwableSupplier.get())));
  }

and the usage:

myMono.transform(m -> errorIfEmpty(m, () -> new RuntimeException()))

or just

errorIfEmpty(myMono, () -> new RuntimeException())

Noticing this hasn't had any activity in a while, but I'd throw my +1 in the mix for this to be in the core library. Wanting a Mono to return exactly one (rather than at least one) element is a common enough scenario that I'd say this deserves its own top level operator (or is it just me that does this often?!)

transform(this::errorIfEmpty) and similar workarounds are of course fine functionally, but at a quick glance it's much less readable than a dedicated "orElseThrow()` or similar method, which makes it instantly clear what's going on.

@berry120 notice that there are kinda two issues here: making the empty case an error and lazily choosing said error. This issue is about the later, since there is the single() operator that propagates NoSuchElementException in case the source is empty.

Btw this makes me think that the error-of-ones-choosing-on-empty full capability can be achieved with:

Mono.empty()
    .single()
    .onErrorMap(NoSuchElementException.class, e -> new MyOwnException("message", e))

@simonbasle

This issue is about the later, since there is the single() operator that propagates NoSuchElementException in case the source is empty.

Oh dear... how that operator could have passed me by I don't know. That's exactly what I was looking for. I'll retreat to my corner again :-)

It makes a bit more sense (in terms of naming) from the Flux perspective. Having an exact mirror version on Mono is maybe not as descriptive in hindsight, but eh.

Was this page helpful?
0 / 5 - 0 ratings