Separate an expected operation failure from an unhandled programming error. Convert expected failures into an explicit result at the boundary that knows how to recover. If catching broadly, rethrow CancellationException so leaving the screen can still stop work.
These two requests are required together. The enclosing scope gives them one failure boundary:
suspend fun loadDashboard(): Dashboard = coroutineScope {
val profile = async { repository.loadProfile() }
val lessons = async { repository.loadLessons() }
Dashboard(profile.await(), lessons.await())
}
// Inside the caller's coroutine:
try {
showDashboard(loadDashboard())
} catch (cancelled: CancellationException) {
throw cancelled
} catch (failure: IOException) {
showRetry()
}
The repository, model and rendering functions are application-specific. If a required child fails, the ordinary scope cancels its siblings and throws to the caller.
async stores its failure in a Deferred, but a failing child can also cancel its ordinary parent immediately. Catching only a later await() does not undo that cancellation. A CoroutineExceptionHandler reports an uncaught failure at an appropriate root or supervised launch boundary; it cannot resume the failed coroutine.
What if recommendations are optional? Give that operation its own explicit recovery policy, using supervision where independent child failure is intended. Do not broadly hide all exceptions as an empty recommendation list.
Mark this when you can explain the answer in your own words.
Help fellow developers prepare for interviews
Sharing helps the Android community grow ๐