Colton Idle
12/12/2019, 9:31 AMval work1 = async { getThingsDone(43) }
val work2 = async { getThingsDoneAgain(123) }
if (work1.await().isSuccessful && work2.await().isSuccessful) {
//do my thing
} else {
//failure message
}
Now with that block above I saw that I was sometimes getting some IOException, and so I wrapped it all in a try/catch.
try {
val work1 = async { getThingsDone(43) }
val work2 = async { getThingsDoneAgain(123) }
if (work1.await().isSuccessful && work2.await().isSuccessful) {
//do my thing
} else {
//failure message
}
} catch (e: IOException){
//network failure message
}
But I still get the IOException. It's as if the try/catch doesn't work.trathschlag
12/12/2019, 9:36 AMCoroutineScope
, so if it fails with an exception, so will your surrounding scope.try {
coroutineScope {
val work1 = async { getThingsDone(43) }
val work2 = async { getThingsDoneAgain(123) }
[...]
}
catch ([...]
octylFractal
12/12/2019, 9:39 AMasync
gets cancelled, it must cancel the parentsupervisorScope
, rather than coroutineScope
, to prevent it from cancelling all parent jobstrathschlag
12/12/2019, 9:42 AMColton Idle
12/12/2019, 9:42 AMtrathschlag
12/12/2019, 9:45 AMcoroutineScope
will be cancelled, but the builder function waits until the scope created by coroutineScope
is done (exceptionally or not).
The reason is simple: If you just do
try {
async { //stuff }
} catch (e: Exception) {
[...]
}
The stuff in async can actually fail, after your current coroutine has left the try clause.Colton Idle
12/12/2019, 9:54 AMstreetsofboston
12/12/2019, 9:54 AMtrathschlag
12/12/2019, 9:58 AMval downloadA = async {
try {
// download
} catch (e: Exception) {
null
}
}
val downloadB = async {
try {
// download
} catch (e: Exception) {
null
}
}
downloadA.await()?.let { value ->
println("successfully downloaded $value")
}
downloadB.await()?.let { value ->
println("successfully downloaded $value")
}
?Colton Idle
12/12/2019, 9:59 AMtry {
viewLifecycleOwner.lifecycleScope.launch {
val work1 = async { getThingsDone(43) }
val work2 = async { getThingsDoneAgain(123) }
if (work1.await().isSuccessful && work2.await().isSuccessful) {
//do my thing
} else {
//failure message
}
} catch (e: Exception) {
//show failure message
}
trathschlag
12/12/2019, 10:02 AMcoroutineScope
builder inside your launch
which will create a new scope and wait for it (and all its children) to finishColton Idle
12/12/2019, 10:05 AMviewLifecycleOwner.lifecycleScope.launch
isn't a "scope"?Anders Mikkelsen
12/12/2019, 10:20 AMtrathschlag
12/13/2019, 8:42 AMawaitAll
will not be able to run, if a failing async
kills your scope before.viewLifecycleOwner.lifecycleScope.launch
is, but I assume it will concurrently start a coroutine so you have the same problem as before: If you try-catch around it, you are actually just try-catching the creation of the coroutine and then immediately proceeding in your code. Your started coroutine might fail 10 seconds later which you can't handle anymore. coroutineScope
creates a new scope and waits for this scope and all it's children (e.g. all `launch`s and `async`s etc...) to complete. If any of these fails, the method will return and throw the causing exception.Anders Mikkelsen
12/13/2019, 8:48 AMtrathschlag
12/13/2019, 8:50 AMAnders Mikkelsen
12/13/2019, 9:01 AMfun main() = runBlocking {
val list = runCatching {
collectExternals()
}.onFailure {
it.printStackTrace()
}.getOrElse { emptyList() }
println(list)
}
suspend fun collectExternals(): List<String> = withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
val one = async(start = CoroutineStart.LAZY) { testFun() }
val two = async(start = CoroutineStart.LAZY) { testFun() }
awaitAll(one, two)
}
fun testFun(): String {
Thread.sleep(1000)
throw IllegalStateException()
}
Then you can "try catch on the coroutine", but if you want to keep any successfull results, youd have to catch the exception in the async, return a null and mapToNull or something like that.streetsofboston
12/13/2019, 9:37 AMasync
than the calling CoroutineScope.