Eric Lin
Eric Lin

Reputation: 33

Async jobs cancellation causes parent cancellation

I tried to run two async jobs.

There is a button when clicked, would cancel one of the jobs. But I noticed when I do it, the other job will be cancelled too.

What happened?

class SplashFragment : BaseFragment(R.layout.fragment_splash), CoroutineScope by MainScope() {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    launch {
        val countdown = async { countDown() }
        val ipAndMaintain = async { checkIPAndMaintain() }

        btnSkip.onClick {
            countdown.cancel() // cancel countdown
            btnSkip.isVisible = false
            if (!ipAndMaintain.isCompleted) {
                showLoadingDialog()
            }
        }
        countdown.await()
        startOtherPage(ipAndMaintain.await())
    }
}

private suspend fun countDown() {
    var time = 3
    while (time >= 0) {
        btnSkip.text = getString(R.string.skip, time)
        delay(1000)
        yield()
        time--
    }
}

private suspend fun checkIPAndMaintain(): Int {
    delay(2000)
    return 1
}

}

Upvotes: 3

Views: 914

Answers (1)

Glenn Sandoval
Glenn Sandoval

Reputation: 3745

When you call await on a cancelled Deferred it throws an Exception. If you don't catch it then it will be propagated to the parent coroutine which will cancel all its children. Wrap your countdown.await() instruction with a try-catch block and you'll see that the other coroutine continues. That's the effect of structured concurrency.

You can read this article by Roman Elizarov about the topic.

Upvotes: 6

Related Questions