Kotlin

Lesson 06

Coroutine scope lifecycle

Tie child coroutines to the request or screen that started the work.

Good Code

src/main/kotlin/reviews/DigestService.kt
suspend fun buildDigest(userId: UserId): Digest = coroutineScope {
    // Child jobs cancel when the digest request is cancelled.
    val user = async { users.find(userId) }
    val reviews = async { reviewStore.recentFor(userId) }

    Digest(user.await(), reviews.await())
}

Bad Code

DigestService.kt
fun buildDigest(userId: UserId) {
    // GlobalScope outlives the request that triggered the digest.
    GlobalScope.launch {
        val reviews = reviewStore.recentFor(userId)
        mailer.sendDigest(userId, reviews)
    }
}

Review Notes

What to review

Good Code

The good version uses coroutineScope, so async work belongs to the suspending request and cancellation reaches both child jobs.

Bad Code

The bad version launches work in GlobalScope, so a cancelled request can leave mail work running without the caller seeing failure.

Takeaways

  • Kotlin coroutine review should check which scope owns each child job and how cancellation reaches it.