Kotlin
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Null Safety
-
Section 4: Object-Oriented Kotlin
-
Section 5: Functional Kotlin
-
Section 6: Coroutines
-
Section 7: Collections Deep Dive
-
Section 8: Type System Deep Dive
-
Section 9: Interop and Tooling
-
Section 10: Kotlin DSLs and Patterns
-
Section 11: Testing and Quality
-
Section 12: Server-Side Kotlin
-
Section 13: Practical Projects
-
Section 14: Interview Practice
-
Section 15: More Practice Exercises
-
Section 16: More Standard Library
-
Section 17: Multiplatform Kotlin
-
Section 18: kotlin.collections In Depth
-
Section 19: kotlin.text In Depth
-
Section 20: kotlin.ranges and kotlin.sequences
-
Section 21: kotlin.io and File Handling
-
Section 22: kotlinx.coroutines Deep Dive
-
Section 23: kotlin.reflect
-
Section 24: Android Development with Kotlin Overview
-
Section 25: Kotlin Multiplatform Deep Dive
-
Section 26: Kotlin for Backend Deep Dive
-
Section 27: Kotlin Design Patterns
-
Section 28: Advanced Language Features
-
Section 29: More Practice Exercises
-
Section 30: More Interview Practice
-
Section 31: Kotlin Type System Deep Dive
-
Section 32: Kotlin Null Safety Advanced
-
Section 33: Kotlin Testing Deep Dive
-
Section 34: Kotlin Build Tooling Deep Dive
-
Section 35: Kotlin Serialization
-
Section 36: Kotlin Performance Considerations
-
Section 37: Kotlin Native Overview
-
Section 38: Kotlin for Data and Scripting
-
Section 39: More Coroutines Practice
-
Section 40: More Android-Adjacent Patterns
-
Section 41: More Practical Projects
-
Section 42: More Design and Architecture Practice
-
Section 43: Kotlin Language Evolution
-
Section 44: More Interview and Review
-
Section 45: Kotlin Delegation Patterns Deep Dive
-
Section 46: Kotlin Annotations Deep Dive
-
Section 47: Kotlin for Gradle Plugin Development
-
Section 48: Kotlin Concurrency Beyond Coroutines
-
Section 49: Kotlin Compiler Internals
-
Section 50: Real-World Kotlin Case Studies
-
Section 51: Final Practice Projects
-
Section 52: Kotlin for Server-Side Reactive Programming
-
Section 53: More Practice and Drills
-
Section 54: Kotlin Security Practices
33: Coroutine Builders: launch and async
Up until now, we've talked about suspend functions, but those are just the building blocks. They are like tools in a toolbox; they don't actually do anything until you create a coroutine. That's where coroutine builders come in. Specifically, launch and async.
The easiest way to distinguish them is to ask yourself: "Do I need a value back from this operation?" If the answer is "No, just go do it," you want launch. If the answer is "Yes, I need the result to move forward," you want async.
Let's build a small "User Profile Dashboard" loader to see this in action. Imagine we need to do three things when a user opens their profile: log the visit for analytics, fetch their basic user info, and fetch their recent posts.
Firing off a background ping with launch
First, let's handle the analytics. I don't want the user to wait for a logging server to respond before they see their profile. This is a classic "fire-and-forget" scenario. I'll use launch because it returns a Job, which allows me to cancel the task if needed, but it doesn't return a result from the block.
suspend fun loadDashboard() = coroutineScope {
// I'm using launch because I don't care when the log finishes
launch {
logVisitToAnalytics()
println("Analytics ping sent.")
}
println("Moving on to data fetching...")
}
Fetching data in parallel with async
Now for the heavy lifting. I need the user's profile data and their posts. If I just call two suspend functions one after the other, they'll run sequentially. If each takes 500ms, the user waits a full second. That's a waste of time since these two requests don't depend on each other.
I'll use async here. Unlike launch, async returns a Deferred<T>, which is basically a promise that a value will be delivered eventually.
suspend fun loadDashboard() = coroutineScope {
launch { logVisitToAnalytics() }
// Start both requests at the same time
val userDeferred = async { fetchUserProfile() }
val postsDeferred = async { fetchUserPosts() }
// Now we wait for the results
val user = userDeferred.await()
val posts = postsDeferred.await()
println("Loaded ${user.name} with ${posts.size} posts.")
}
The "Deferred" mistake
When I first started using async, I made a silly mistake that you might be tempted to make too. I tried to use the result of the async block immediately without calling .await().
I wrote something like this:
val user = async { fetchUserProfile() }
println("Hello, ${user.name}") // Error!
The compiler yelled at me because user wasn't a User object; it was a Deferred<User>. I was trying to access a property on the "promise" rather than the "result." It's a common trip-up. Just remember: async starts the work, but await() is what actually pauses the coroutine until the value is ready and then hands that value back to you.
Coordinating the final result
In the finished version of our loader, we've got a nice balance. The analytics happen in the background via launch, and the two data fetches happen concurrently via async. The total time spent waiting is only as long as the slowest network call, not the sum of all of them.
suspend fun loadDashboard() = coroutineScope {
// Fire and forget
launch { logVisitToAnalytics() }
// Concurrent execution
val userDeferred = async { fetchUserProfile() }
val postsDeferred = async { fetchUserPosts() }
// Wait for both to finish
val user = userDeferred.await()
val posts = postsDeferred.await()
renderProfile(user, posts)
}📋 Practical Task
Build a Concurrent Weather and News Aggregator
You are building a "Morning Briefing" app. You need to create a function called getMorningBriefing() that performs the following tasks:
- Log the request: Use
launchto call a fakelogBriefingRequest()function (fire-and-forget). - Fetch Weather: Use
asyncto callfetchWeather(), which returns aString. - Fetch News: Use
asyncto callfetchTopNews(), which returns aList<String>.
The function must wait for both the weather and news to arrive before returning a formatted string that combines both results. Ensure you use coroutineScope to wrap your builders. If you forget await(), your code won't compile, so pay close attention to the return types!
There are no comments for now.