Skip to content

Instantly share code, notes, and snippets.

@DrMetallius
Last active November 9, 2020 12:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DrMetallius/06655c456e5ef60ea74d6b11ddc5d050 to your computer and use it in GitHub Desktop.
Save DrMetallius/06655c456e5ef60ea74d6b11ddc5d050 to your computer and use it in GitHub Desktop.
Exceptions in coroutines
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
import kotlinx.coroutines.runBlocking
val scope = CoroutineScope(Dispatchers.Default)
val supervisorScope = scope + SupervisorJob()
val exceptionHandler = CoroutineExceptionHandler { _, throwable -> println("Caught exception: $throwable") }
/*
* - Если есть родитель - всегда в него
* - Если есть CoroutineExceptionHandler - используется он
* - Если async, оборачивает в результат
* - Иначе выбрасывается
*
* Когда ловите исключения в отменяемых сопрограммах, пропускайте CancellationException
* supervisorScope, когда нужно, чтобы одна сопрограмма не останавливала другую
*/
fun main() = runBlocking {
runLaunch()
}
suspend fun runLaunch() {
val job = scope.launch { failingTask() }
job.join()
}
suspend fun runAsync() {
val deferred = scope.async { failingTask() }
deferred.join()
}
suspend fun runNestedLaunch() {
val job = scope.launch {
launch { failingTask() }
launch { heavyTask() }
launch { veryHeavyTask() }
}
job.join()
}
suspend fun runNestedLaunchWithHandler() {
val job = scope.launch(exceptionHandler) {
launch { failingTask() }
launch { heavyTask() }
launch { veryHeavyTask() }
}
job.join()
}
suspend fun runNestedLaunchWithHandlerInside() {
val job = scope.launch {
launch(exceptionHandler) { failingTask() }
launch { heavyTask() }
launch { veryHeavyTask() }
}
job.join()
}
suspend fun runNestedAsync() {
val job = scope.launch {
val deferred = async {
failingTask()
true
}
launch { heavyTask() }
launch { veryHeavyTask() }
println("Result: ${deferred.await()}")
}
job.join()
}
suspend fun runNestedAsyncWithTryCatchIncorrectly() {
val job = scope.launch {
val deferred = async {
failingTask()
true
}
launch { heavyTask() }
launch { veryHeavyTask() }
val result = try {
deferred.await()
} catch (ex: Exception) {
println("Caught $ex")
false
}
println("Result: $result")
}
job.join()
}
suspend fun runNestedLaunchWithTryCatchIncorrectly() {
val job = scope.launch {
try {
launch { failingTask() }
} catch (ex: Exception) {
println("Caught $ex!")
}
launch { heavyTask() }
launch { veryHeavyTask() }
}
job.join()
}
suspend fun runNestedLaunchWithTryCatchWithoutCancellation() {
val job = scope.launch {
launch {
try {
failingTask()
} catch (ex: Exception) {
println("Caught $ex")
}
println("At least we tried!")
}
launch { heavyTask() }
launch { veryHeavyTask() }
}
job.join()
}
suspend fun runNestedLaunchWithTryCatchWithoutCancellationHandlingAndCanceling() {
val job = scope.launch {
val innerJob = launch {
try {
failingTask()
} catch (ex: Exception) {
println("Caught $ex!")
}
println("At least we tried!")
}
launch { heavyTask() }
launch { veryHeavyTask() }
innerJob.cancel()
}
job.join()
}
suspend fun runNestedLaunchWithTryCatchAndCanceling() {
val job = scope.launch {
val innerJob = launch {
try {
failingTask()
} catch (ex: Exception) {
if (ex is CancellationException) throw ex
println("Caught $ex!")
}
println("At least we tried!")
}
launch { heavyTask() }
launch { veryHeavyTask() }
innerJob.cancel()
}
job.join()
}
suspend fun runOnRegularScope() {
val job = scope.launch { failingTask() }
val otherJob = scope.launch { heavyTask() }
job.join()
println("Waiting for the job")
otherJob.join()
println("Waiting for the other job")
}
suspend fun runOnSupervisorScope() {
val job = supervisorScope.launch { failingTask() }
val otherJob = supervisorScope.launch { heavyTask() }
job.join()
println("Waiting for the job")
otherJob.join()
println("Waiting for the other job")
}
suspend fun heavyTask() {
delay(5000)
println("Heavy task finished!")
}
suspend fun veryHeavyTask() {
delay(15000)
println("Very heavy task finished!")
}
suspend fun failingTask() {
delay(1000)
throw Exception("It failed!")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment