Basic concepts about Kotlin Coroutine – Coroutine and Suspend functions

coroutine-suspend-function

What are suspend functions

In Kotlin, a suspend function is a special function used for asynchronous programming. It can pause and resume later without blocking the thread, making coroutines efficient for handling long-running tasks.

Key Features:

  • Non-blocking Pauses: A suspend function can pause without stopping the entire thread, allowing other tasks to run while waiting (e.g., for a network request or file operation).
  • Used with Coroutines: It must be called from another suspend function or within a coroutine. Coroutines help manage asynchronous tasks in a structured way.
  • Easy to Read: Although it works asynchronously, a suspend function looks like a regular function, making the code simple and easy to understand—without callbacks.

When you add the suspend keyword to a function, it means the function can pause its execution.

  • While paused, the thread is free to do other tasks instead of waiting.
  • Later, the function resumes from where it stopped and continues running.

Syntax of suspend function

suspend fun functionName(parameters): ReturnType {
  // Function body
}

For example:

suspend fun fetchData(url: String): String {
  // Simulate a long-running operation (like network request)
  delay(1000) // Suspends the coroutine without blocking the thread
  return "Data from $url"
}
  • The suspend keyword makes a function suspendable.
  • delay(1000) pauses the function for 1 second without blocking the main thread.
  • A suspend function can only be called from another suspend function or inside a coroutine.

How does suspend functions work internally?

When the Kotlin compiler sees a suspend function, it transforms it using Continuation-Passing Style (CPS) to handle asynchronous execution.

Continuation Mechanism

  • Suspend functions use continuations. Instead of running all at once, they can pause and save their state.
  • This saved state, called a continuation, keeps track of where the function stopped, including variables and execution context.
  • Later, the function resumes from the same point, continuing its execution without losing progress.

For example:

suspend fun fetchData(): String {
  delay(1000) // Suspend here
  return "Data"
}

When delay(1000) is called, the function suspends, and the continuation is saved. After 1000 milliseconds, the continuation resumes execution.

State Machine

The Kotlin compiler creates a state machine for a suspend function. This state machine keeps track of where the function paused and resumes it later. It handles suspension and resumption without blocking threads, making execution more efficient.

Coroutine Context

The coroutine context decides which thread or dispatcher a coroutine runs on. When a suspend function is called, it runs inside a coroutine context that can switch threads when needed.

For example:

  • Dispatchers.IO runs tasks in the background (e.g., network calls, file operations).
  • Dispatchers.Main runs tasks on the main thread (e.g., updating the UI).

For example:

GlobalScope.launch(Dispatchers.IO) {
  val data = fetchData() // fetchData is a suspend function
}

So we have,

  • Suspension: Pauses the function without blocking the thread and saves its state.
  • Continuation: Resumes the function from where it stopped, keeping its execution context.
  • State Machine: Tracks and manages the function’s execution steps.
  • Coroutine Context: Controls which thread or dispatcher the coroutine runs on, allowing smooth thread switching.

Step-by-step breakdown of how a suspend function works

Declaring a Suspend Function

When you create a suspend function, Kotlin automatically generates a state machine to handle pausing and resuming the function.

suspend fun fetchData(): String {
  delay(1000L) // Suspends the function for 1 second
  return "Data fetched"
}

Continuation Object Creation

When a suspend function runs, Kotlin creates a Continuation object to manage its pause and resume process. This object stores:

  • The function’s current state (where it stopped).
  • The result or error after resuming.

Suspension Point

When a function reaches a suspension point (like a delay or network call), it pauses instead of blocking the thread. The Continuation object saves its state and lets the thread do other work.

For example, delay(1000L) pauses the function and gives control back to the caller.

Resuming with Continuation

After the asynchronous operation (like delay) finishes, the Continuation object helps resume the function from where it paused. The function then continues running as if it was never stopped. For example:

continuation.resumeWith(Result.success("Data fetched"))

What Happens Internally:

  • The function resumes from where it paused (after delay).
  • It then returns the result: "Data fetched".

State Machine Works

Kotlin turns a suspend function into a state machine. Each suspension point represents a different state, and the continuation object ensures the function resumes from the right state after pausing.

Here’s a simple example in pseudo-code showing how states change:

fun fetchData(continuation: Continuation<String>) {
  when (continuation.state) {
    0 -> {
      // Initial state: suspend the coroutine and wait
      continuation.state = 1
      delay(1000L)
    }
    1 -> {
      // Resume state: continuation resumes after delay
      continuation.resumeWith(Result.success("Data fetched"))
    }
  }
}

Coroutine Dispatcher

A coroutine can run on different threads. The CoroutineDispatcher controls which thread it runs on after suspension. For example:

  • A network request may suspend the coroutine on an I/O thread.
  • After completion, it resumes on the main thread to update the UI.
launch(Dispatchers.IO) {
  val result = fetchData()
  withContext(Dispatchers.Main) {
    println(result) // Switch back to the main thread after suspension
  }
}

Flow Summary:

  1. Function Call: The suspend function starts.
  2. Continuation Created: A continuation object keeps track of the function’s state.
  3. Function Suspends: At suspension points (e.g., delay()), the function pauses, and the continuation handles resumption.
  4. Async Task Runs: While suspended, tasks like network requests run in the background.
  5. Function Resumes: After the task is done, the function resumes using the continuation.
  6. Return Result: The function completes and returns the final result.

 

Leave a Reply

Your email address will not be published. Required fields are marked *