4
votes

I have 3 snippet below

  1. Just scope launch
    fun main() = CoroutineScope(Dispatchers.IO).launch { runMe() }
    fun rumMe() = doSomething()
  1. scope launch with suspend
    fun main() = CoroutineScope(Dispatchers.IO).launch { runMe() }
    suspend fun rumMe() = doSomething()
  1. scope launch with suspend and withContext
    fun main() = CoroutineScope(Dispatchers.IO).launch { runMe() }
    suspend fun rumMe() = withContext(Dispatchers.Default) { doSomething() }

I see them being launch in a different thread from the Main, and is run asynchronously not blocking the main thread.

I wonder what's their different? If they are all the same, then 1 is best. If not, when should I use 2 or 3?

I tried reading this, and can't get it clearly https://medium.com/@elizarov/blocking-threads-suspending-coroutines-d33e11bf4761

2

2 Answers

3
votes

1 and 2 are the same. You have to add suspend modifier to your function only if it does something with coroutines.

The difference between the first and the third cases:

fun main() = CoroutineScope(Dispatchers.IO).launch { 
    // io thead executor
    runMe()
}
// still io thread executor
fun rumMe() = doSomething()

fun main() = CoroutineScope(Dispatchers.IO).launch { 
    // io thead executor
    runMe()
}
suspend fun rumMe() = withContext(Dispatchers.Default) { 
    // default/cpu thead executor
    doSomething()
}
1
votes

By adding suspend modifier to a function, you allow the function to use another suspending functions. For example, if the runMe() function would use suspending delay it's reasonable to make that function suspendable. See Your first coroutine documentation section.

Another major difference of suspending functions over ordinary ones, is that the suspending functions are cancellable. Let's look at the Android example:

class MyViewModel : BaseViewModel() {
  init {
    viewModelScope.launch {
      val firstPart = loadFirstPartOfData()
      val secondPart = loadSecondPartOfData(firstPart)
      ...
    }
  }

  suspend loadFirstPartOfData() = withContext(Dispatchers.IO) { ... }

  suspend loadSecondPartOfData(firstPartOfData: FirstPartOfData) {
    // some UI thread safe calculations
    return withContext(Dispatchers.IO) {
        // network request here
    }
  }
}

Imagine that the view (Android Activity) loads the data to display it. If the Activity is closed before the second part of data is loaded, it's wasteful to load the second part. But, because the loadSecondPartOfData() function is suspending, it checks whether the scope is active or not, and the function won't be executed if the scope isn't active.

Also notice how the functions use withContext(Dispatchers.IO). The functions are called from viewModelScope.launch, which by default uses Dispatchers.Main (UI thread), but it's safe to call the functions from UI thread because the execution context is explicitly selected by the functions. It's the right way to write suspending functions, when you don't worry about the current thread when you call a function. It's regarding your third question.

In your example, the first snippet will work, but in a real app, as usual, things become a little bit more complex.