OverView of Coroutines

 · 4 min read
 · Nima Moradi
Table of contents

Introduction

Coroutines are a powerful control flow mechanism that enable asynchronous programming without dramatically restructuring code like traditional callback-based approaches. Both Python and Kotlin provide native support for coroutines, offering developers elegant solutions for tasks like:

  • Network I/O: Handling multiple network requests concurrently.
  • File I/O: Efficient reading and writing of files.
  • Long-running Computations: Breaking down complex tasks into smaller, manageable units that can pause and resume.
  • Other Asynchronous Scenarios: Wherever non-blocking operations are needed.

Key Concepts

Suspending Functions:

  • Python: Functions declared using the async keyword. They use the await keyword to suspend execution and wait for the result of another coroutine.
  • Kotlin: Functions declared using the suspend keyword. Similarly, use various suspend functions to pause execution without blocking a thread.

Event Loop:

  • Python: The asyncio library manages an event loop, responsible for scheduling and executing coroutines.
  • Kotlin: The kotlinx coroutines library provides coroutine builders and an underlying mechanism for their management. But here we use the Android's coroutine library. Syntax and Structure

Syntax and Structure

On Android, coroutines help to manage long-running tasks that might otherwise block the main thread and cause your app to become unresponsive. In this example we send a request to database using kotlin, the io-request should always be done in background thread, so we use the coroutine to handle this task.

import android.os.AsyncTask
import kotlinx.coroutines.*

// Simulate a simple database interaction 
class MyDatabase {
    suspend fun fetchData(query: String): String {
        delay(2000L) // Simulate database operation delay
        return "Result for query: $query"
    }
}

class MyActivity : AppCompatActivity() { // Assuming you are in an Activity context

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        lifecycleScope.launch { // Launch coroutine within the Activity's lifecycle
            val result1 = fetchDataFromDB("SELECT * FROM table1")
            val result2 = fetchDataFromDB("SELECT * FROM table2")
           // update ui
        }
    }

    private suspend fun fetchDataFromDB(query: String): String = withContext(Dispatchers.IO) {
        MyDatabase().fetchData(query)
    }
}

Explanation

lifecycleScope.launch: Launches the coroutine in the context of the Activity's lifecycle, ensuring cancellation when the Activity is destroyed. withContext(Dispatchers.IO): Switches the coroutine to a background thread suitable for database operations. This is crucial to avoid blocking the main thread.

In Python we can use the asyncio library to handle the coroutine, here is an example of how to use the asyncio library to request from database.

import asyncio
import mysql.connector

mydb = mysql.connector.connect(
    host="your_database_host",
    user="your_username",
    password="your_password",
    database="your_database_name"
)

async def fetch_data(query):
    cursor = mydb.cursor()
    cursor.execute(query)

    result = cursor.fetchall()  # Fetch all results (adjust as needed)
    cursor.close()  # Close the cursor

    return result 

async def main():
    # Example usage
    result1 = await fetch_data("SELECT * FROM table1") 
    print(result1)

asyncio.run(main())

Structured Concurrency

Handling coroutine lifecycles and errors is critical:

  • Python: Structured concurrency is primarily done using asyncio.gather and asyncio.wait to manage concurrent tasks, along with try-except blocks.
  • Kotlin: Provides coroutineScope and supervisorScope for creating structured hierarchies of coroutines, enabling fine-grained error handling and cancellation.
Feature Python Kotlin
Keyword async suspend
Event Loop Lib asyncio k otlinx.coroutines
Starting Point asyncio.run(...) runBlocking

Conclusion

Both Python and Kotlin provide effective coroutine implementations, enabling developers to write cleaner, more manageable asynchronous code. Coroutines are a powerful tool for handling I/O-bound tasks, long-running computations, and other asynchronous scenarios, making them a valuable addition to any developer's toolkit.