Kotlin Coroutine Supervisor ile Hata Yakalama ve İş Yönetimi

Murat YÜKSEKTEPE
3 min readAug 12, 2024

--

Bir ana Coroutine içinde birden fazla Coroutine çalıştırıyor olalım:

import kotlinx.coroutines.*

fun main() {
runBlocking {
launch { println(doSomeWork(1)) }
launch { println(doSomeWork(2)) }
launch { println(doSomeWork(3)) }
}
}

suspend fun doSomeWork(number: Int): String {
delay(1000)
return "Cevap: $number"
}

Bu alt işlerden birisi başarısız olduğu zaman ana Coroutine’in de başarısız olarak tamamlandığını ve sırasını bekleyen diğer işlerin devam edemediğini görürüz.

import kotlinx.coroutines.*

fun main() {
runBlocking {
launch { println(doSomeWork(1)) }
launch { println(doSomeWork(2)) }
launch { println(doSomeWork(3)) }
}
}

suspend fun doSomeWork(number: Int): String {
delay(1000)
if (number == 2) throw Exception("$number numaralı iş başarısız sonuçlandı.")
return "Cevap: $number"
}

Alt işlerden birisinin başarısız olduğu zaman diğer işlerin sekteye uğramadan çalışmaya devam etmesini istiyorsak SupervisorJob()’ı kullanmalıyız. Bunun için izleyeceğimiz bir kaç yöntem mevcut.

Try-Catch

Bu yöntemde başarısız olacağından şüphelendiğiniz işi try-catch bloguyla çevreleriz. Ama hangi işin başarısız olacağına emin olamayız. O yüzden her alt işi try-catch’e almak gerekir. Bu da çirkin ve uygun olmayan bir kod yapısı ortaya çıkaracaktır.

import kotlinx.coroutines.*

fun main() {
runBlocking {
launch { println(doSomeWork(1)) }
launch {
try {
println(doSomeWork(2))
} catch (e: Exception) {
println("2. numaralı iş başarısız oldu!")
}
}
launch { println(doSomeWork(3)) }
}
}

suspend fun doSomeWork(number: Int): String {
delay(1000)
if (number == 2) throw Exception("$number numaralı iş başarısız sonuçlandı.")
return "Cevap: $number"
}

SupervisorJob()

Bu yöntem, hem ana işi hem de her alt işi başlatırken parametre olarak SupervisorJob() kullanarak yapılır. SupervisorJob() ana iş parçasına bağlı alt iş parçalarının birinin başarısız olma durumunda diğer işlerin bundan etkilenmeden devam edebilmelerini sağlar.

import kotlinx.coroutines.*

suspend fun main() {
val coroutineExceptionHandler = CoroutineExceptionHandler { context, exception ->
println("Bir sorun var! - ${exception.localizedMessage}")
println("==============================")
}

val supervisorJob = SupervisorJob()

CoroutineScope(supervisorJob + coroutineExceptionHandler).launch {
launch(supervisorJob) { println(doSomeWork(1)) }.join()
launch(supervisorJob) { println(doSomeWork(2)) }.join()
launch(supervisorJob) { println(doSomeWork(3)) }.join()
}.join()
}

suspend fun doSomeWork(number: Int): String {
delay(1000)
if (number == 2) throw Exception("$number numaralı iş başarısız sonuçlandı.")
return "Cevap: $number"
}

supervisorScope{}

Burada bahsedeceğimiz sonuncu yöntem ise kod açısından en temiz ve en kısa yöntem olan supervisorScope yöntemidir. Bu yöntem de tüm alt iş parçaları supervisorScope gövdesi içine taşınır ve Exception durumlarını yönetebilmek için sadece ana iş parçasına CoroutineExceptionHandler tanımlamak yeterli olacaktır.

Başarısız olan alt iş diğerlerini (ve doğal olarak ana iş parçasını) etkileyemeyecek ve Exception hatalarını kolay bir şekilde yakalıyor ve yönetebiliyor olacağız.

import kotlinx.coroutines.*

suspend fun main() {
val coroutineExceptionHandler = CoroutineExceptionHandler { context, exception ->
println("Bir sorun var! - ${exception.localizedMessage}")
println("==============================")
}

CoroutineScope(coroutineExceptionHandler).launch {
supervisorScope{
launch { println(doSomeWork(1)) }.join()
launch { println(doSomeWork(2)) }.join()
launch { println(doSomeWork(3)) }.join()
}
}.join()
}

suspend fun doSomeWork(number: Int): String {
delay(1000)
if (number == 2) throw Exception("$number numaralı iş başarısız sonuçlandı.")
return "Cevap: $number"
}

--

--