# Idiomatic way to cancel coroutine from within a suspend function

**URL:** <https://discuss.kotlinlang.org/t/idiomatic-way-to-cancel-coroutine-from-within-a-suspend-function/5268>\
**Category:** Uncategorized\
**Created:** [November 4, 2017, 5:44pm UTC](https://discuss.kotlinlang.org/t/idiomatic-way-to-cancel-coroutine-from-within-a-suspend-function/5268 "2017-11-04T17:44:17Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![fatjoe79](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/fatjoe79/32/4221_2.png) [@fatjoe79](https://discuss.kotlinlang.org/u/fatjoe79)\
**Post date:** [November 4, 2017, 5:44pm UTC](https://discuss.kotlinlang.org/t/idiomatic-way-to-cancel-coroutine-from-within-a-suspend-function/5268/1 "2017-11-04T17:44:17Z")

</div>

I have written a little API that is capable of handling exceptions that occur within a coroutine. The basic implementation of this API goes as follows:

```
interface ErrorHandler {
    suspend fun handleException(error: Exception): ErrorHandling
}

enum class ErrorHandling { RETRY, RETHROW, CANCEL }

suspend fun <T> withErrorHandler(errorHandler: ErrorHandler, c: suspend () -> T): T {
    loop@while (true) {
        try {
            return c();
        } catch (e: Exception) {
            when (errorHandler.handleException(e)) {
                RETRY -> continue@loop
                RETHROW -> throw e
                CANCEL -> TODO("Find a way to cancel the coroutine (and its parents?)")
            }
        }
    }
}

```

Examples of implementations of the ErrorHandler interface are:

**ExponentialBackoffErrorHandler**  
For some error types, this error handler will return RETRY after some exponentially growing delay. It will return RETHROW when max retries are reached.

**DialogErrorHandler**  
Shows an error dialog to the user. For some error types, the dialog can include buttons for RETRY’ing or CANCEL’ing the execution.

Example of how the API can be used:

```
launch (UI) {
    val foo = withErrorHandler(dialogErrorHandler) {
        serviceApi.getFoo(fooId).await()
    }

    val bar = withErrorHandler(dialogErrorHandler) {
        serviceApi.getBar(foo.barId).await()
    }

    updateUI(bar)
}

```

My question is how to cancel the coroutine in the most idiomatic way, when the user chooses CANCEL from within the error dialog (see TODO in the above code). Simply returning will not work, since then the coroutine would continue and expect some result, which will not be available.

---

<div class="post-metadata">

**Author:** ![elizarov](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/elizarov/32/2164_2.png) [@elizarov](https://discuss.kotlinlang.org/u/elizarov)\
**Post date:** [November 5, 2017, 3:24pm UTC](https://discuss.kotlinlang.org/t/idiomatic-way-to-cancel-coroutine-from-within-a-suspend-function/5268/2 "2017-11-05T15:24:52Z")

</div>

From inside the coroutines you can do `coroutineContext.cancel()`. The only missing part, for now, is that `coroutineContext` is not yet available inside regular suspending functions (only inside builders like `launch` and `async`). It will be soon, though. See this ticket. It also has a work-around: [https://youtrack.jetbrains.com/issue/KT-17609](https://youtrack.jetbrains.com/issue/KT-17609)

---

<div class="post-metadata">

**Author:** ![fatjoe79](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/fatjoe79/32/4221_2.png) [@fatjoe79](https://discuss.kotlinlang.org/u/fatjoe79)\
**Post date:** [November 5, 2017, 4:01pm UTC](https://discuss.kotlinlang.org/t/idiomatic-way-to-cancel-coroutine-from-within-a-suspend-function/5268/3 "2017-11-05T16:01:44Z")

</div>

That’s great, thank you.

I would like to express my devotion to you and your colleagues at Jetbrains. You are Rockstars. The James Goslings and Neal Gafters of this decade.

---

<div class="post-metadata">

**Author:** ![fatjoe79](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/fatjoe79/32/4221_2.png) [@fatjoe79](https://discuss.kotlinlang.org/u/fatjoe79)\
**Post date:** [November 6, 2017, 10:18am UTC](https://discuss.kotlinlang.org/t/idiomatic-way-to-cancel-coroutine-from-within-a-suspend-function/5268/4 "2017-11-06T10:18:12Z")

</div>

I tried implementing the suggestion. The code now looks as follows:

```
suspend fun <T> withErrorHandler(errorHandler: ErrorHandler, block: suspend () -> T): T {
    loop@while (true) {
        try {
            return block();
        } catch (e: Exception) {
            when (errorHandler.handleException(e)) {
                RETRY -> continue@loop
                RETHROW -> throw e

                // Cancel the coroutine as suggested:
                CANCEL -> coroutineContext().cancel() 
            }
        }
    }
}

```

Invoking `coroutineContext().cancel()` indeed cancels the coroutine. But of course it does not abort the execution of my while loop. The while loop will try to execute the block one more time, which will yield a _JobCancellationException: Job was cancelled normally_. This in turn will be handled again by my dialogErrorHandler, which will show another dialog for the cancellation exception

I am not sure whether I fully understand what is happening here. I understand why the JobCancellationException is thrown. But I do not understand, why am I still allowed to invoke the suspending handleException function? Shouldn’t this yield another cancellation exception?

Nevertheless, what is the best way to fix this?

Possible solutions:

- replace the `while(true)` with `while(isActive)`. Problem: this way the compiler will complain that I need to return a result after the while loop (and I do not have a result).

- Check whether the Exception is a JobCancellationException (or its parent CancellationException), and re-throw it instead of invoking the handleException function. Problem: seems to be less ideal, as it will execute the while loop one last time, knowing that it will just yield the exception.

- After canceling the coroutine, throw an exception myself. Can I throw a CancellationException myself for this purpose? Do I even need to cancel the coroutine before throwing this exception, or can I simply just throw the CancellationException without invoking `coroutineContext().cancel()` before?

---

<div class="post-metadata">

**Author:** ![elizarov](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/elizarov/32/2164_2.png) [@elizarov](https://discuss.kotlinlang.org/u/elizarov)\
**Post date:** [November 6, 2017, 10:32am UTC](https://discuss.kotlinlang.org/t/idiomatic-way-to-cancel-coroutine-from-within-a-suspend-function/5268/5 "2017-11-06T10:32:08Z")

</div>

You should indeed loop `while (isActive)` and you should throw exception when it is cancelled. You can either rely on some other cancellable suspending function to throw it for you (`delay` and `yield` both throw it when the job is cancelled) or throw it yourself using `throw job.getCancellationException()`.

---

<div class="post-metadata">

**Author:** ![fatjoe79](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/fatjoe79/32/4221_2.png) [@fatjoe79](https://discuss.kotlinlang.org/u/fatjoe79)\
**Post date:** [November 6, 2017, 10:48am UTC](https://discuss.kotlinlang.org/t/idiomatic-way-to-cancel-coroutine-from-within-a-suspend-function/5268/6 "2017-11-06T10:48:48Z")

</div>

Since the `withErrorHandler` function needs to return a result, it seems like I need to throw the exception myself. I did this now with

```
throw coroutineContext()[Job]?.getCancellationException() ?: CancellationException()

```

I am not sure about whether my handling of the nullable `coroutineContext()[Job]` makes any sense. Maybe there is another way to retrieve the current coroutine’s non-null Job. Anyway, this seems to work now in my tests so far.

---

<div class="post-metadata">

**Author:** ![elizarov](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/elizarov/32/2164_2.png) [@elizarov](https://discuss.kotlinlang.org/u/elizarov)\
**Post date:** [November 6, 2017, 3:46pm UTC](https://discuss.kotlinlang.org/t/idiomatic-way-to-cancel-coroutine-from-within-a-suspend-function/5268/7 "2017-11-06T15:46:23Z")

</div>

You can do `coroutineContext()[Job]!!.getCancellationException()`. As long as you always using coroutine builders from `kotlinx.coroutines` library (like `launch` and `async`) there is always a `Job` in the context. Alternatively, you can just use `yield()` and you will not have to think about it at all.

---

<div class="post-metadata">

**Author:** ![fatjoe79](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/fatjoe79/32/4221_2.png) [@fatjoe79](https://discuss.kotlinlang.org/u/fatjoe79)\
**Post date:** [November 6, 2017, 4:10pm UTC](https://discuss.kotlinlang.org/t/idiomatic-way-to-cancel-coroutine-from-within-a-suspend-function/5268/8 "2017-11-06T16:10:55Z")

</div>

I cannot use `yield()` as it will not solve the problem that I need to return a value of type T in my `withErrorHandler` function. In the case of CANCEL, I don’t have a value to return, so I can only throw an Exception. Otherwise the compiler will complain that I need to return a T.

---

<div class="post-metadata">

**Author:** ![elizarov](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/elizarov/32/2164_2.png) [@elizarov](https://discuss.kotlinlang.org/u/elizarov)\
**Post date:** [November 6, 2017, 4:25pm UTC](https://discuss.kotlinlang.org/t/idiomatic-way-to-cancel-coroutine-from-within-a-suspend-function/5268/9 "2017-11-06T16:25:45Z")

</div>

You can use this pattern:

```auto
while (true) {
    // try return result here

   yield() // before looping, will check for cancel, too
}

```

---

<div class="post-metadata">

**Author:** ![fatjoe79](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/fatjoe79/32/4221_2.png) [@fatjoe79](https://discuss.kotlinlang.org/u/fatjoe79)\
**Post date:** [November 6, 2017, 4:50pm UTC](https://discuss.kotlinlang.org/t/idiomatic-way-to-cancel-coroutine-from-within-a-suspend-function/5268/10 "2017-11-06T16:50:59Z")

</div>

Yes it will work with while(true), but it will not work with while(isActive). The compiler will complain that I need to return a T after the while loop.
