25

I am a beginner studying coroutines.

Not exactly, but I have a little understanding of what a coroutine is.

The suspend function is also difficult, but with a little understanding.

I'm studying step by step, but there are parts I don't understand.

That's suspendCoroutine. In the sample code, suspendCoroutine and Continuation used in the block appear, but I have no idea what these two are.

I've looked on other sites, but I can't find anywhere that explains it easily.

Can you explain what suspendCoroutine and Continuation are used for easily and, if possible, with an example?

Sergio
  • 27,326
  • 8
  • 128
  • 149
ybybyb
  • 1,385
  • 1
  • 12
  • 33
  • 1
    I found these slides to be helpful https://www.slideshare.net/elizarov/introduction-to-coroutines-kotlinconf-2017 – Ankur Feb 07 '22 at 20:39
  • 1
    Note that continuations and `suspendCoroutine()` are pretty much advanced stuff. They are definitely not the easiest to understand in the whole coroutines machinery. – broot Feb 07 '22 at 21:59
  • 1
    Although using `suspendCoroutine` is pretty straightforward, truly understanding how it does the magic it does can be mind-bending. I suggest [this answer](https://stackoverflow.com/a/48112934/1103872) as one way to gain that insight. – Marko Topolnik Feb 08 '22 at 09:37

2 Answers2

58

suspendCoroutine is a builder function that mainly used to convert callbacks into suspend functions. Let's say for example you have some legacy (or not) Api, that uses callbacks. You can easily transform it into a suspend function to call it in a coroutine. For example:

suspend fun getUser(id: String): User  = suspendCoroutine { continuation ->
      Api.getUser(id) { user ->
          continuation.resume(user)
      }
}

Here we have an Api function getUser, which defined in Api class for example like this:

fun getUser(id: String, callback: (User) -> Unit) {...}

suspendCoroutine suspends coroutine in which it executed until we decide to continue by calling appropriate methods - Continuation.resume.... suspendCoroutine mainly used when we have some legacy code with callbacks.

Using suspendCoroutine to convert callbacks into suspend functions makes the code sequential when you work with suspend functions.

For example instead of having a callback hell like this:

Api.getUser(id) { user ->
      Api.getProfile(user) { profile ->
          Api.downloadImage(profile.imageId) { image ->
              // ...
          }
      } 
}

after you apply suspendCoroutine to those callbacks and convert them into suspend functions, the code will look like the following:

val user = getUser(id)
val profile = getProfile(user)
val image = downloadImage(profile.imageId)
//...
Sergio
  • 27,326
  • 8
  • 128
  • 149
  • I understand a little bit, but it's still difficult.Let me ask you a question. `1.` You said you could convert it to a `suspend function` to call from a `coroutine`. Isn't it possible to use regular functions within a `coroutine`? Why use it as a `suspend function`? – ybybyb Feb 08 '22 at 20:41
  • `2.` You said `suspendCortuine` is a builder that converts `callbacks` into `suspend functions`. So, if I put a callback function in the block(`{}`) of `suspendCoroutine` in the first code, does the `callback function` become a `suspend function`?In this case, `Api.getUser` is a callback function, so is it a `suspend function`?`3.` How can the last callback codes be expressed as `suspendCoroutine`? – ybybyb Feb 08 '22 at 20:43
  • 1
    1. The point of coroutines is to run some code in concurrently without blocking the Main Thread. Using `suspend` functions makes your code sequential and asynchronous. You can call not `suspend` functions in a coroutine, but it makes no sense, you can call them without a coroutine. – Sergio Feb 08 '22 at 21:10
  • 1
    2. No, the original function doesn't become a `suspend` function. Suspend functions are marked with `suspend` keyword, like this: `suspend fun getProfile()`. When you use `suspendCoroutine` you create another function, which becomes `suspend`, see my example: `Api.getUser(...)` is not suspend, but `suspend fun getUser(...)` is suspend, it is two different function with the same purpose, the last one calls the first one. – Sergio Feb 08 '22 at 21:15
  • I see.. I will ask the question while checking the OP's code slowly from the beginning.. `A.` In the first code, `suspend fun getUser(id: String)` is not a function created with `suspendCoroutine`. It is just a normal suspend function. Right? and I'm trying to make the `callback API.getUser()` a suspend function using `suspendCoroutine`. Is that right? – ybybyb Feb 09 '22 at 19:00
  • `B.` Is the first code correct for applying `suspendCoroutine` in the `callback hell` code? However, the first code does not appear for the `getProfile()` and `downloadImage()` apis. Should I apply` suspendCoroutine` to each of these two functions as in the first code? `C.` Is it correct that `getProfile()` and `downloadImage()` with `suspendCoroutine` applied in the last code are also written like the first code? – ybybyb Feb 09 '22 at 19:01
  • 4
    I don't understand your questions. There are a lot of docs and tutorials on YouTube about `suspendCoroutine`, for example this one https://www.youtube.com/watch?v=OmHePYcHbyQ – Sergio Feb 09 '22 at 19:10
  • In the last code, `val user = getUser(id)` is the `suspend fun getUser(id: String): User = suspendCoroutine { ... }` of the first code, right? If correct, `getUser()` is a `suspend function`, so why use `suspendCoroutine` in return again? Can't we just use it in this format? `suspend fun getUser(id: String): User { return Api.getUser(id) }` – ybybyb Feb 13 '22 at 19:37
  • No, `Api.getUser(id)` doesn't return anything. – Sergio Feb 13 '22 at 19:51
5

suspendCoroutine is Kotlin's name for Scheme's function call-with-current-continuation (abr. call/cc).

It is explained in this video "Fresh Async With Kotlin • Roman Elizarov • GOTO 2018".

The article "The Yin-yang puzzle in Kotlin" explains it based on the Yin-yang puzzle, which is a famous example use case for call/cc. Other use cases are explained in the article "Continuations by example: Exceptions, time-traveling search, generators, threads, and coroutines".

ceving
  • 21,900
  • 13
  • 104
  • 178