GettingNiceToHaveDataInAsyncWithoutCrashingRequiredDataGet
Pattern for getting nice to have data asynchronously without crashing the scope if nice to have data fails.
Code
package com.glassthought.sandbox
import com.glassthought.sandbox.util.out.impl.out
import gt.sandbox.util.output.Out
import kotlinx.coroutines.*
fun main() = runBlocking {
initialize()
}
private suspend fun initialize() {
try {
// Use supervisor scope so that if async throws an exception prior to getting to await
// we do not fail the entire scope.
supervisorScope {
val niceToHaveData = async(CoroutineName("queryNiceToHaveData")) {
out.actionWithMsg("niceToHaveData", {
throw FailureToGetNiceToHaveData("Failed to fetch niceToHaveData")
})
"nice-to-have-data-val"
}
val parseNotesDeferred = async(CoroutineName("required-data-2")) {
out.actionWithMsg("required-data", {
delay(100)
"required-data-1-val"
})
}
val niceToHaveResult = niceToHaveData.awaitOrNull(out)
val requiredData1 = parseNotesDeferred.await()
out.actionWithMsg("required-data-2", {
out.info("niceToHaveDataResult:$niceToHaveResult")
out.info("requiredDataResult:" + requiredData1)
})
}
} catch (e: CancellationException) {
out.cancelled("initialize", e)
} catch (e: Throwable) {
out.error("Initialize caught ERROR $e")
}
}
private suspend fun <T> Deferred<T>.awaitOrNull(out: Out): T? {
try {
return this.await()
} catch (e: CancellationException) {
out.cancelled("awaitOrNull", e)
throw e
} catch (e: Exception) {
out.warn("awaitOrNull caught [${e::class.simpleName}/${e.message}], returning null")
return null
}
}
class FailureToGetNiceToHaveData(msg: String) : RuntimeException(msg)
Command to reproduce:
gt.sandbox.checkout.commit 8b69f4ce79c906ff14c1 \
&& cd "${GT_SANDBOX_REPO}" \
&& cmd.run.announce "./gradlew run --quiet"
Recorded output of command:
Picked up JAVA_TOOL_OPTIONS: -Dkotlinx.coroutines.debug
Picked up JAVA_TOOL_OPTIONS: -Dkotlinx.coroutines.debug
[INFO][elapsed: 28ms][①][coroutname:@queryNiceToHaveData#2] [->] action=[niceToHaveData] is starting.
[WARN][elapsed: 71ms][①][coroutname:@queryNiceToHaveData#2] [<-][💥] Finished action=[niceToHaveData], it THREW exception of type=[FailureToGetNiceToHaveData] we are rethrowing it.
[INFO][elapsed: 85ms][⓶][coroutname:@required-data-2#3] [->] action=[required-data] is starting.
[WARN][elapsed: 86ms][⓷][coroutname:@coroutine#1] awaitOrNull caught [FailureToGetNiceToHaveData/Failed to fetch niceToHaveData], returning null
[INFO][elapsed: 186ms][⓶][coroutname:@required-data-2#3] [<-] Finished action=[required-data].
[INFO][elapsed: 187ms][⓷][coroutname:@coroutine#1] [->] action=[required-data-2] is starting.
[INFO][elapsed: 187ms][⓷][coroutname:@coroutine#1] niceToHaveDataResult:null
[INFO][elapsed: 187ms][⓷][coroutname:@coroutine#1] requiredDataResult:required-data-1-val
[INFO][elapsed: 187ms][⓷][coroutname:@coroutine#1] [<-] Finished action=[required-data-2].
Backlinks