Suspending functions should not be called on a different dispatcher
Tags: android, viewmodel, codequality, sonarlint
Published: 2023-08-14 05:56:46

---
title: Suspending functions should not be called on a different dispatcher
published: true
cover_image: https://s3-us-west-2.amazonaws.com/my-blogresources/IMAG1121.jpg
description: Use best practices to avoid using different dispatchers for suspending functions
tags: android, viewModel, quality, sonarlint
series: Quality Quest
---

### Why

There seems to be confusion around using Dispatchers in Coroutines. Dispatchers are used to mention the thread on which the coroutine should run.

  - `Dispatchers.Main` - Main thread
  - `Dispatchers.IO` - IO thread
  - `Dispatchers.Default` - CPU-intensive thread

While we should mention the dispatcher for the coroutine based on the work a function does, We don't need to do the same for the caller of the suspending function. i.e., The suspend function that does the actual work handles what dispatcher to use, and the caller of the function should not worry about it.

In fact, various libraries explicitly provide suspending APIs for long-running blocking operations. The complexity of moving the appropriate tasks to background threads is already taken care of within the library. When calling the library's suspending API, it does not have to be considered.

**P.S:** `Dispatchers.Unconfined` is not recommended to be used in production code and is not discussed here.

### What to do

- When you write a long-running or CPU intensive task, wrap the code with the appropriate dispatcher so that the caller of the function need not worry about it.
- If the library provides suspending APIs, the library will take care of the dispatchers. (It's a good practice to check the documentation of the library to see if it provides suspending APIs)
- If you are calling an existing suspending function, check if it uses the appropriate dispatcher. If not, wrap it with appropriate dispatcher to avoid mentioning one in the caller of the function.

## How to do

Here's an example:

```kotlin
fun onDoneClicked() {
    viewModelScope.launch(Dispatcher.IO) {
        initiateSeeding()
    }
}

...

suspend fun initiateSeeding() {
    async {
        // Do some work that runs in IO thread
    }
}
```

1. Wrap the long-running IO task with `Dispatcher.IO`

    ```kotlin

    suspend fun initiateSeeding() {
        async(Dispatcher.IO) {
            // Do some work that runs in IO thread
        }
    }
    ```

2. Remove the dispatcher from the caller of the function

    ```kotlin
    fun onDoneClicked() {
        viewModelScope.launch {
            initiateSeeding()
        }
    }
    ```

Another good example is Retrofit. Retrofit provides suspending APIs for network calls. The library takes care of the dispatchers, and the caller of the function need not worry about it.

```kotlin
interface SeedApiService {
    @GET("seeds")
    suspend fun getSeeds(): List<Seed>
}

class SeedRepository(private val seedApiService: SeedApiService) {
    suspend fun getSeeds(): List<Seed> = seedApiService.getSeeds()
}

class SeedViewModel(private val seedRepository: SeedRepository) {
    fun fetchSeeds() {
        viewModelScope.launch {
          try {
            val seeds = seedRepository.getSeeds()
            // Do something with the seeds
          } catch (e: Exception) {
            // Handle error
          }
        }
    }
}
```

The library will handle the dispatchers, and We don't need to mention it in the `viewModelScope.launch` function.

## References

- [Retrofit Suspended Functions](https://amitshekhar.me/blog/retrofit-with-kotlin-coroutines)
- [Do-i-need-to-call-suspend-functions-of-retrofit-and-room-on-a-background-thread](https://www.lukaslechner.com/do-i-need-to-call-suspend-functions-of-retrofit-and-room-on-a-background-thread/)

---