Intro to Kotlin Coroutines
Kotlin Coroutines for Beginners: Learn with Examples
If you’re new to Kotlin coroutines, you might feel overwhelmed by the concept of asynchronous programming. But don’t worry! Coroutines are here to make your life easier. In this blog post, we’ll start from the very basics and build up your understanding with simple examples.
What Are Coroutines?
Coroutines are like lightweight threads. They allow you to perform tasks asynchronously (in the background) without blocking the main thread. This is especially useful for tasks like:
- Fetching data from the internet
- Reading/writing to a database
- Performing heavy computations
The best part? Coroutines make your code look sequential and easy to read, even though it’s running asynchronously.
Key Concepts
Before we dive into examples, let’s understand a few key terms:
- Coroutine: A piece of code that runs asynchronously.
- Suspend Function: A function that can pause its execution and resume later without blocking the thread.
- Scope: A way to manage the lifecycle of coroutines (e.g.,
GlobalScope,runBlocking). - Dispatcher: Determines which thread the coroutine runs on (e.g.,
Dispatchers.Main,Dispatchers.IO).
Example 1: Hello World with Coroutines
Let’s start with the simplest example: printing “Hello, World!” using a coroutine.
import kotlinx.coroutines.*
fun main() {
println("Start")
// Launch a coroutine
GlobalScope.launch {
delay(1000L) // Pause for 1 second
println("Hello, World!")
}
println("End")
Thread.sleep(2000L) // Keep the main thread alive
}
Output:
Start
End
Hello, World!
Explanation:
GlobalScope.launchstarts a new coroutine.delay(1000L)pauses the coroutine for 1 second without blocking the main thread.Thread.sleep(2000L)keeps the main thread alive so the coroutine can finish.
Example 2: Using runBlocking
In the previous example, we used Thread.sleep to keep the main thread alive. But this isn’t ideal. Instead, we can use runBlocking, which creates a coroutine scope and blocks the main thread until the coroutine finishes.
import kotlinx.coroutines.*
fun main() = runBlocking {
println("Start")
launch {
delay(1000L)
println("Hello from coroutine!")
}
println("End")
}
Output:
Start
End
Hello from coroutine!
Explanation:
runBlockingcreates a coroutine scope and blocks the main thread.launchstarts a new coroutine inside therunBlockingscope.- The coroutine pauses for 1 second and then prints “Hello from coroutine!”.
Example 3: Suspending Functions
A suspending function is a function that can pause its execution and resume later. Let’s create a suspending function to simulate a network request.
import kotlinx.coroutines.*
fun main() = runBlocking {
println("Start")
launch {
val result = fetchData()
println(result)
}
println("End")
}
// Suspending function
suspend fun fetchData(): String {
delay(2000L) // Simulate a network request
return "Data fetched!"
}
Output:
Start
End
Data fetched!
Explanation:
fetchData()is a suspending function that simulates a network request by pausing for 2 seconds.- The
launchcoroutine callsfetchData()and prints the result.
Example 4: Running Multiple Coroutines
You can run multiple coroutines concurrently. Let’s simulate two tasks running at the same time.
import kotlinx.coroutines.*
fun main() = runBlocking {
println("Start")
launch {
delay(1000L)
println("Task 1 done!")
}
launch {
delay(1500L)
println("Task 2 done!")
}
println("End")
}
Output:
Start
End
Task 1 done!
Task 2 done!
Explanation:
- Two coroutines are launched concurrently.
- The first coroutine finishes after 1 second, and the second finishes after 1.5 seconds.
Example 5: Using async and await
If you need to perform multiple tasks concurrently and wait for their results, use async and await.
import kotlinx.coroutines.*
fun main() = runBlocking {
println("Start")
val result1 = async {
delay(1000L)
"Result 1"
}
val result2 = async {
delay(1500L)
"Result 2"
}
println("End")
println("${result1.await()} and ${result2.await()}")
}
Output:
Start
End
Result 1 and Result 2
Explanation:
asyncstarts a coroutine and returns aDeferredobject, which is a promise of a future result.await()waits for the result of theDeferredobject.- Both coroutines run concurrently, and their results are printed at the end.
Example 6: Using Dispatchers
Coroutines can run on different threads using dispatchers. For example, you can use Dispatchers.IO for network requests or Dispatchers.Main for UI updates.
import kotlinx.coroutines.*
fun main() = runBlocking {
println("Start on thread: ${Thread.currentThread().name}")
launch(Dispatchers.Default) {
println("Running on thread: ${Thread.currentThread().name}")
delay(1000L)
println("Task done!")
}
println("End on thread: ${Thread.currentThread().name}")
}
Output:
Start on thread: main
End on thread: main
Running on thread: DefaultDispatcher-worker-1
Task done!
Explanation:
Dispatchers.Defaultruns the coroutine on a background thread.- The main thread is not blocked, and the coroutine runs concurrently.
Example 7: Handling Errors
Coroutines provide a way to handle errors using try-catch.
import kotlinx.coroutines.*
fun main() = runBlocking {
println("Start")
launch {
try {
delay(1000L)
throw RuntimeException("Something went wrong!")
} catch (e: Exception) {
println("Error: ${e.message}")
}
}
println("End")
}
Output:
Start
End
Error: Something went wrong!
Explanation:
- The coroutine throws an exception after 1 second.
- The exception is caught and handled in the
catchblock.
Summary
Here’s what we’ve learned:
- Coroutines are lightweight and great for asynchronous tasks.
- Use
launchto start a coroutine anddelayto pause it. - Use
asyncandawaitfor concurrent tasks. - Use
Dispatchersto control which thread the coroutine runs on. - Handle errors with
try-catch.
With these examples, you should have a solid foundation to start using Kotlin coroutines in your projects. Practice these concepts, and soon you’ll be writing efficient and clean asynchronous code!
Happy coding! 🚀