Suppose I have a (spring boot controller method): ...
# coroutines
p
Suppose I have a (spring boot controller method):
Copy code
getLineById(...) = runBlocking {
  Deferred<SomeType> someDeferred = async { dependant.thatCanThrow() }

  SomeType? res = someDeferred.awaitOrNull();
}

suspend inline fun <T> Deferred<T>.awaitOrNull(): T? = runCatching { this.await() }.getOrNull()
Shouldn't my
awaitOrNull
catch all exceptions and convert them to null? The problem arises when testing:
Copy code
@MockkBean
private lateinit var dependent: Dependent

   @SpykBean
    private lateinit var controller: MainController

every { dependent.thatCanThrow() } throws Exception("omg") 

controller.getLineById(...)
then the test just throws the Exception("omg"), seemingly pointing to the line:
Copy code
every { dependent.thatCanThrow() } throws Exception("omg")
s
An exception from a failed
async
job always propagates to the containing scope (in this case the
runBlocking
scope) in addition to being thrown from
await
. So I would expect
getLineById
to fail if the async job fails.
p
Ok, so what should I do if I would like to ignore all errors thrown by someDeferred and just get a null then?
s
You could put the error handling inside the
async
job itself, and make it a
Deferred<SomeType?>
p
So instead of
Copy code
Deferred<SomeType> someDeferred = async { dependant.thatCanThrow() }
I should write
Copy code
Deferred<SomeType> someDeferred = async { try { dependant.thatCanThrow() } catch(e: Exception) { null } }
? Can I write an extension method to make it more compact?
say
asyncOrNull
?
Is this method correct?
Copy code
public fun <T> CoroutineScope.asyncOrNull(
        context: CoroutineContext = EmptyCoroutineContext,
        start: CoroutineStart = CoroutineStart.DEFAULT,
        block: suspend CoroutineScope.() -> T
    ): Deferred<T?> = async(context, start) {
        try {
            block()
        } catch (e: Exception) {
            null
        }
    }
s
Looks good to me 👍 😎