kotlin async await vs lanch co-routine

 Kotlin launch is a co-routine operating under the model 'fire-and-forget'. It returns a Job object that we can use to see if it is active or cancel it. It is also non-blocking. 


import kotlinx.coroutines.*

fun main() = runBlocking {
    // Launch a background coroutine
    val job = launch {
        delay(1000L)
        println("World!")
    }
   
    println("Hello,") // This prints immediately while 'World!' is waiting
    job.join()        // (Optional) Wait for the launch block to finish
}

If you want to fetch user data from a network API and use that data, launch won't cut it because it can't return the data. You must use async, which is where await() comes into play:

This is a good example of launch use-case 


class ProfileViewModel : ViewModel() {

    // viewModelScope automatically cancels the launch if the screen closes
    fun updateProfilePicture(imagePath: String) {
        viewModelScope.launch(Dispatchers.IO) {
            // 1. Move to the I/O thread pool
            uploadImage(imagePath)
           
            // 2. Fire-and-forget background work
            logAnalytics("Image Uploaded")
        }
    }
}
```</T>

We shift the entire code to another job. 


async and await 

Whenever we would like to use asynchronous task and possible return results, we can use  async/await and a very simple example would look like this:- 


Approach 1: General approach



import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.coroutines.delay

// 1. Define the asynchronous function using 'suspend'
suspend fun fetchUserData(userId: String): String {
    // optional: force this work onto the I/O thread pool
    return withContext(Dispatchers.IO) {
        delay(2000L) // Simulating a 2-second network request
        "User Profile Data for $userId"
    }
}

And to call it, we use the following code:-




fun main() = runBlocking {
    println("Fetching user...")
   
    // Looks like synchronous code, but it is completely asynchronous!
    val result = fetchUserData("123")
   
    println("Result received: $result")
}


Approach 2: Explicit Parallelism (Using async { })

Using async keyword to call async function 

import kotlinx.coroutines.*

suspend fun getStockPrice(): Int {
    delay(1000) // Simulate network
    return 50
}

suspend fun getCryptoPrice(): Int {
    delay(1000) // Simulate network
    return 60000
}

fun main() = runBlocking {
    println("Starting parallel fetches...")
   
    // Both start executing at the exact same time
    val stockDeferred: Deferred<Int> = async { getStockPrice() }
    val cryptoDeferred: Deferred<Int> = async { getCryptoPrice() }
   
    // Wait for both to finish and get results using .await()
    val totalPortfolio = stockDeferred.await() + cryptoDeferred.await()
   
    println("Total value: \$${totalPortfolio}")
    // Total time elapsed is ~1 second, not 2 seconds!
}


3. Creating Extension Functions that Return Deferred and forcing developer to call .await()

In languages like JavaScript, when you call an async function, a Promise is spun up into the global space. If the user navigates away or closes a screen, that Promise keeps running in the background, causing memory leaks or waste.

Kotlin prevents this through CoroutineScope. Every coroutine must belong to a scope. If the scope dies (e.g., the user closes an Android screen or a Ktor HTTP request finishes), all coroutines inside that scope are automatically canceled.


import kotlinx.coroutines.*
// This function can only be called if a CoroutineScope is available
fun CoroutineScope.fetchDataAsync(): Deferred<String> = async {
    delay(1000)
    "Async Result"
}


You are saying: "This function is structurally bound to whoever called it." It forces the function to inherit the caller's lifecycle, ensuring it can never become a rogue background task.











Comments