Coroutines

Coroutines are more general than subroutines. They can be used to implement iterators, generators, and other control structures that require cooperative multitasking.

Multitasking

Multitasking is the general term for managing multiple tasks in a simulation or in solving a problem.

When it comes to implementation, there are two kinds:

Coroutines

Coroutines are a form of cooperative multitasking. They allow multiple entry points for suspending and resuming execution at certain points.

In many programming languages, coroutines are implemented using constructs like yield or await, which allow a function to pause its execution and return control to the caller, while maintaining its state for later resumption.

Python Examples

def coroutine_example():
    print("Coroutine started")
    value = yield  # Yield control back to the caller
    print(f"Received value: {value}")
    yield  # Yield again, allowing further interaction
    print("Coroutine finished")
coro = coroutine_example()
next(coro)  # Start the coroutine
coro.send(42)  # Send a value to the coroutine
next(coro)  # Resume the coroutine

Examples in Lua

nextSquare = coroutine.create(function ()
  for value = 1, 5 do
    coroutine.yield(value * value)
  end
  return "Thank you"
end)

for i = 1, 8 do
  local status = coroutine.status(nextSquare)
  local success, values = coroutine.resume(nextSquare)
  print(status, success, values)
end
nextSquare = coroutine.wrap(function ()
  for value = 1, 5 do
    coroutine.yield(value * value)
  end
  return "Thank you"
end)

for i = 1, 8 do
  local success, value = pcall(nextSquare)
  print(success, value)
end

Examples in Kotlin

@file:DependsOn("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
import kotlinx.coroutines.*

runBlocking {
    repeat(5) {
        launch {
            println("Coroutine $it running on ${Thread.currentThread().name}.")
            delay((1000L..5000L).random())
            println("Coroutine $it finished on ${Thread.currentThread().name}.")
        }
    }
    println("All coroutines launched")
}
@file:DependsOn(
    "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1",
    "com.squareup.okhttp3:okhttp:4.11.0",
    "org.json:json:20230618",
)

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.json.JSONObject

val baseUrl = "https://pokeapi.co/api/v2/pokemon/"
val pokemonNames = listOf("ditto", "pikachu", "mew", "weedle", "eevee")

data class PokeInfo(val name: String, val height: Int, val weight: Int)

suspend fun fetchPokemon(name: String) {
    println("Fetching $name on ${Thread.currentThread().name}")
    val request = Request.Builder().url("$baseUrl$name").build()
    val response = OkHttpClient().newCall(request).execute()
    if (!response.isSuccessful) {
        println("Cannot fetch $name: HTTP ${response.code}")
        return
    }
    val jsonObject = JSONObject(response.body?.string() ?: return)
    println(PokeInfo(
        jsonObject.getString("name"),
        jsonObject.getInt("height"),
        jsonObject.getInt("weight")))
}

runBlocking {
    for (name in pokemonNames) {
        launch(Dispatchers.IO) {
            fetchPokemon(name)
        }
    }
    println("The fetches are underway")
}
println("The fetches are done")

Use Cases

Coroutines are useful in various scenarios:

Recall Practice

Here are some questions useful for your spaced repetition learning. Many of the answers are not found on this page. Some will have popped up in lecture. Others will require you to do your own research.

Coming soon

Summary

We’ve covered:

  • ...
  • ...