https://kotlinlang.org logo
#coroutines
Title
# coroutines
c

Colton Idle

12/15/2019, 7:58 PM
Okay. I haven't had any luck on adding exception handling to my coroutine. Anyone out there that knows how to prevent this from crashing?
Copy code
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
        }
Basically, the getThingsDone calls are retrofit network calls. If one of them is still going on and I go into airplane mode, I get a crash. What have I tried? 1.
Copy code
try {
    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
}
2.
Copy code
viewLifecycleOwner.lifecycleScope.launch {
            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: Exception) {
                //show failure message
            }
        }
3.
Copy code
viewLifecycleOwner.lifecycleScope.launch {
        val work1 : Deferred<Response<MyType>>
        val work2: Deferred<Response<MyType>>
    try {
        work1 = async { getThingsDone(43) }
        work2 = async { getThingsDoneAgain(123) }
    } catch (e: Exception) {
        //show failure message
    }
        if (work1.await().isSuccessful && work2.await().isSuccessful) {
            //do my thing
        } else {
            //failure message
        }
}
4.
Copy code
viewLifecycleOwner.lifecycleScope.launch {
    val work1 = async {
        try {
            getThingsDone(43)
        } catch (e: Exception) {
            //show failure message
        }
    }
    val work2 = async {
        try {
            getThingsDoneAgain(123)
        } catch (e: Exception) {
            //show failure message
        }
    }
    
    if (work1.await().isSuccessful && work2.await().isSuccessful) {
        //do my thing
    } else {
        //failure message
    }
}
I would really appreciate some help. As of now this works super nice because I essentially have two async network calls, and my code ONLY continues if both of them complete! But in the off chance that I have a network error (or force one by going into airplane mode during the request), my Android app dies.
s

streetsofboston

12/15/2019, 8:10 PM
c

Colton Idle

12/15/2019, 8:18 PM
I tried following along with that article a few days ago and I wasn't able to help myself with it. Either something is going over my head or I'm completely missing something basic here.
s

streetsofboston

12/15/2019, 8:22 PM
Copy code
val ceh = CoroutineExceptionHandler { c, t -> ..... handle the exception here ... }
viewLifecycleOwner.lifecycleScope.launch(ceh) { ...
    ...
}
c

Colton Idle

12/15/2019, 8:29 PM
🤦 Thank you Anton. That seems so simple now. I think in the other thread that had close to 30 replies got me confused and got me thinking too much about forcing a try catch. (some people in that chat said that they got it working). The CEH makes sense to me. Just for my own knowledge though, do you know if there's any way to get a try catch working in this case? Or is that not possible? I'm only asking because I'm on a team with newer members, and I feel like try/catch would be easier to reason about, but the CEH is also pretty easy. So it's a win/win in my book. Thank you again. I've been pulling my hair out on this one for the past few days.
t

trathschlag

12/16/2019, 8:24 AM
As I wrote in the other thread, the easiest method imho is this:
Copy code
viewLifecycleOwner.lifecycleScope.launch {
  try {
    coroutineScope { 
      // all your async stuff goes here
    }
  } catch (e: Exception) {
    // handle error
  }
}
But actually looking at your code I think listing
4
should work, too. So maybe there is another problem we cannot see here.
p

Pablichjenkov

12/16/2019, 4:06 PM
Why number 4 does not work for you? Number 4 is the best approach in general. I prefer catching all my exceptions at the earliest stage possible and convert them to my own domain Result<T> object so error handling is easier. I don't like to rely on CEH or any other coroutine exception mechanincs to handle known exceptions. Better catch early and treat it as a Failure result. I also don't like the fact that one failed
async
causes its siblings and parent death. Try to avoid this as much as possible, if it is a known exception obvious.
c

Colton Idle

12/23/2019, 12:03 AM
@trathschlag @Pablichjenkov 4 definitely did not work for me. Not sure why. I'm away from that project this week and will pick back up with it in 2020 and I will come back. I would expect #4 to work as well.
p

Pablichjenkov

12/23/2019, 12:47 AM
Maybe try catching a Throwable rather than an exception. You should also define a return type from the async lambda that wraps either an error or a value. Don't forget to rethrow the exception if it is of type CancellationException.