Cancelling Coroutines

Betül Necanlı
5 min readMar 12, 2023

--

👉🏻 In Kotlin, a coroutine is a lightweight thread that can be executed concurrently with other coroutines. Coroutines are cooperative, meaning that they have to voluntarily suspend execution to allow other coroutines to run.

Cancelling a coroutine is the process of stopping its execution before it has completed.

Cancelling a coroutine can be useful in several scenarios, such as:

  • Interrupting a long-running operation that is no longer necessary or desirable.
  • Freeing up system resources that were allocated to the coroutine.
  • Stopping a coroutine that has become stuck or unresponsive.

In Kotlin, coroutines can be cancelled using the cancel( ) function. When a coroutine is cancelled, it is immediately suspended, and its isActive flag is set to false. This flag can be used by the coroutine to check if it has been cancelled and take appropriate action.

Example of how to cancel a coroutine using the cancel( ) function:

In this example, a coroutine is launched using the launch function. The coroutine simply prints a message and sleeps for 500 milliseconds, repeating this process 1000 times. After waiting for 1300 milliseconds, the main coroutine cancels the launched coroutine using the cancel( ) function. Finally, the main coroutine waits for the launched coroutine to complete using the join( ) function.

Another way to cancel a coroutine is to use a Job instance. A Job represents a single unit of work that can be cancelled or waited for. Here is an example of how to cancel a coroutine using a Job instance:

In this example, a Job instance is created using the launch function. The coroutine sleeps for 5000 milliseconds before printing a message indicating that it has completed. After waiting for 2000 milliseconds, the main coroutine cancels the job using the cancelAndJoin( ) function, which cancels the job and waits for its completion. Finally, the main coroutine prints a message indicating that it can now quit.

In addition to the cancel( ) and cancelAndJoin( ) functions, there are several other ways to cancel coroutines in Kotlin, such as using a TimeoutCancellationException or a custom cancellation exception. However, the cancel( ) and cancelAndJoin( ) functions are the most commonly used methods for cancelling coroutines.

Cancelling Async

1.Cancelling a single async coroutine:

In this example, a single async coroutine is launched that performs a long-running operation (in this case, simply a delay). After waiting for 2000 milliseconds, the coroutine is cancelled using cancelAndJoin( ).

2. Cancelling multiple async coroutines:

In this example, three async coroutines are launched that perform different long-running operations. After waiting for 4000 milliseconds, all three coroutines are cancelled using cancelAndJoin( ).

3.Cancelling async coroutines in a structured concurrency:

In this example, the supervisorScope function is used to create a scope for the three async coroutines. This means that if any of the coroutines throws an exception, only that coroutine and its children will be cancelled, rather than the entire scope. After waiting for 4000 milliseconds, all three coroutines are cancelled using cancelAndJoin( ).

👉🏻 Note that cancelling a coroutine does not necessarily mean that it will immediately stop running. A coroutine can only be cancelled when it reaches a suspension point, such as a call to delay( ). If a coroutine is cancelled while it is in the middle of executing some code, it will continue to run until it reaches a suspension point, at which point it will be cancelled. It is also possible for a coroutine to ignore a cancellation request if it does not check its isActive flag.

Cancelling Launch

1.Cancelling a single launch coroutine:

In this example, a single launch coroutine is launched that performs a long-running operation (in this case, simply a delay). After waiting for 2000 milliseconds, the coroutine is cancelled using cancel( ) and then join( ) is called to wait for the coroutine to finish.

2.Cancelling multiple launch coroutines:

In this example, three launch coroutines are launched that perform different long-running operations. After waiting for 4000 milliseconds, all three coroutines are cancelled using cancel( ) and then join( ) is called to wait for each coroutine to finish.

3.Cancelling launch coroutines in a structured concurrency:

In this example, the supervisorScope function is used to create a scope for the three launch coroutines. This means that if any of the coroutines throws an exception, only that coroutine and its children will be cancelled, rather than the entire scope. After waiting for 4000 milliseconds, all three coroutines are cancelled using cancel( ).

Note that cancelling a coroutine does not necessarily mean that it will immediately stop running. A coroutine can only be cancelled when it reaches a suspension point, such as a call to delay( ). If a coroutine is cancelled while it is in the middle of executing some code, it will continue to run until it reaches a suspension point, at which point it will be cancelled. It is also possible for a coroutine to ignore a cancellation request if it does not check its isActive flag.

Tips&Tricks 🍬

1.Always check the isActive flag: When you create a long-running coroutine, you should periodically check the isActive flag to see if the coroutine has been cancelled. If the flag is false, you should stop the coroutine as soon as possible. Here's an example:

2.Use the finally block to clean up resources: If your coroutine uses any resources that need to be cleaned up (such as file handles or network sockets), you should put the cleanup code in a finally block. This ensures that the resources are always cleaned up, even if the coroutine is cancelled. Here's an example:

3.Use structured concurrency to manage coroutines: Structured concurrency is a way of organizing your coroutines into hierarchies, so that they can be cancelled as a group. This is useful when you have many coroutines that depend on each other, and you want to make sure that they are all cancelled if any one of them fails. Here’s an example:

In this example, we create a supervisorScope and launch two coroutines. If either coroutine fails, only that coroutine and its children will be cancelled, not the entire scope. We use join( ) to wait for each coroutine to finish.

4.Use timeouts to limit the duration of coroutines: Sometimes you may want to limit the amount of time that a coroutine can run, so that it doesn’t hang indefinitely. You can do this using the withTimeout( ) function. Here's an example:

In this example, we use withTimeout( ) to limit the amount of time that a coroutine can run to 5 seconds. If the coroutine takes longer than 5 seconds, a TimeoutCancellationException will be thrown.

5.Use CoroutineExceptionHandler to handle errors: If a coroutine throws an exception, you can use a CoroutineExceptionHandler to handle the error. This allows you to log the error, display an error message, or take some other action. Here's an example:

In this example, we create a CoroutineExceptionHandler that simply logs any exceptions that are thrown. We then launch a coroutine and pass the handler to it. If the coroutine throws an exception, the handler will be called.

--

--

Betül Necanlı
Betül Necanlı

Written by Betül Necanlı

Kotlin, Android Programming, Data Structures&Algorithms, Math. https://www.youtube.com/@betulnecanli

No responses yet