7

kotlinx-coroutines-reactive makes org.reactivestreams.Publisher to have awaitXXX methods:

val person = peopleReactiveRepository.findById(personId).awaitSingle()

If there is no person can be found by a person ID, this invocation will throw NoSuchElementException and this exception cannot be handled in the user code directly. And Spring MVC ExceptionHandler can not translate this exception into a user-friendly response.

java.util.NoSuchElementException: No value received via onNext for awaitSingle
at kotlinx.coroutines.experimental.reactive.AwaitKt$awaitOne$$inlined$suspendCancellableCoroutine$lambda$1.onComplete(Await.kt:131) ~[kotlinx-coroutines-reactive-0.22.1.jar:na]
at reactor.core.publisher.StrictSubscriber.onComplete(StrictSubscriber.java:123) ~[reactor-core-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onComplete(Operators.java:1327) ~[reactor-core-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at reactor.core.publisher.FluxHide$SuppressFuseableSubscriber.onComplete(FluxHide.java:137) ~[reactor-core-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:130) ~[reactor-core-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at reactor.core.publisher.MonoNext$NextSubscriber.onComplete(MonoNext.java:96) ~[reactor-core-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at com.mongodb.reactivestreams.client.internal.ObservableToPublisher$1.onComplete(ObservableToPublisher.java:78) ~[mongodb-driver-reactivestreams-1.6.0.jar:na]

One of approaches I can figure out is in the following:

val person = peopleRepository.findById(personId).awaitFirstOrDefault(null)

if (person == null) {
    // do something
}

But I do not think it is an elegant way. For example, can provide a method named awaitSingleOptional.

Is there any better Kotlin way to handle this scenario?

Yang Lifan
  • 445
  • 6
  • 15

3 Answers3

6

There are no standard Optional wrappers in Kotlin. You can use the let function for such cases:

val person = peopleRepository.findById(personId).awaitFirstOrDefault(null)?.let {
    // do
}

If the await-expression evaluates to the default null, the let invocation will also evaluate to null. If you need to handle this case, the Elvis operator can be used:

.let {...} ?: throw IllegalStateException()
s1m0nw1
  • 76,759
  • 17
  • 167
  • 196
  • 1
    Using `let` over `Option` is more idiomatic for kotlin, see https://kotlinlang.org/docs/reference/idioms.html which mentions `let` but not `Option`/`Optional` – Hank D Feb 05 '18 at 15:06
3

An Extension awaitFirstOrNull() has been made available in the recent kotlinx.coroutines release 0.22.2. See this PR.

Taken from the release notes:

Reactive: Added awaitFirstOrDefault and awaitFirstOrNull extensions (see #224, PR by @konrad-kaminski).

s1m0nw1
  • 76,759
  • 17
  • 167
  • 196
1

You can definitely use Optional wrappers if you are programming in a functional way.

If you want to get your toes wet with functional programming, you can pick up Arrow which has the Option and the Try data types for this purpose.

Using ?.let is another option but it won't help you much with reactive programming.

There is also the Notification class in ReactiveX which will lets you handle an erroneous scenario if you are doing Railway Oriented Programming.

Adam Arold
  • 29,285
  • 22
  • 112
  • 207