Jaimin Modi
Jaimin Modi

Reputation: 1677

Implementing async- await() in kotlin coroutine

I have created function as below :

fun getPercentage(id:String): String {
    var percentage=""
    scope.launch {
        percentage=repo.getPercentage(id)?.get(0)?.percent.toString()
        Log.e("$$$ value >>","$$$ value >>"+percentage)
    }
    Log.e("$$$ value outside >>","$$$ value >>"+percentage)
    return percenatge
}

Here, I can not return the updated value using the varibale : percentage.

Logs I am getting is as below :

$$$ value outside >> 
$$$ value >> 50

means I can't return the latest value. something going wrong with the flow.

Someone suggested me to use async{} and await(). But I don't know How it will be helpful here?

Please guide. Thanks.

Upvotes: 5

Views: 9811

Answers (2)

Raman
Raman

Reputation: 19685

The getPercentage function creates a coroutine in the background via the launch function, and then continues without waiting. Your "outside" code is therefore running before the "inner" coroutine completes.

The first option is to use the async function instead to return a Deferred value from the coroutine:

fun getPercentage(id:String): Deferred<String> {
    return scope.async {
        percentage=repo.getPercentage(id)?.get(0)?.percent.toString()
        Log.e("$$$ value >>","$$$ value >>"+percentage)
    }
}

Note of course that its more likely you want to make getPercentage a suspend function, and then call await directly:

suspend fun getPercentage(id:String): String {
    val percentageDeferred = scope.async {
        percentage=repo.getPercentage(id)?.get(0)?.percent.toString()
        Log.e("$$$ value >>","$$$ value >>"+percentage)
    }
    val percentage = percentageDeferred.await()
    Log.e("$$$ value outside >>","$$$ value >>"+percentage)
    return percentage    
}

It's also likely you want to do something else before the await otherwise you're probably just better off making repo.getPercentage a suspend function as well, and calling it directly:

suspend fun getPercentage(id:String): String {
    // if repo.getPercentage is a suspend function, this call suspends
    // like the await in the previous example
    val percentage = repo.getPercentage(id)?.get(0)?.percent.toString()
    Log.e("$$$ value outside >>","$$$ value >>"+percentage)
    return percentage    
}

See Concurrent using async in the Kotlin docs.

Upvotes: 8

Milack27
Milack27

Reputation: 1689

I don't believe you necessarily need to use async in this case, particularly. You only need to be aware that whatever is inside launch { ... } is executed asynchronously. So by the time getPercentage returns, your coroutine may not have even started yet.

Keeping that in mind, I believe you may want to change the way your code works. The only way you can make fun getPercentage(id: String): String work without changing that signature is by replacing scope.launch { ... } with scope.runBlocking { ... }, but you probably don't want to do that, because it would block your thread.

Instead, you might change getPercentage to be a suspend method:

suspend fun getPercentage(id: String): String {
    return repo.getPercentage(id)?.get(0)?.percent.toString()
}

However, suspend methods can only be called from inside a coroutine. So you would need to call it like this:

scope.launch {
    val percentage = getPercentage("some ID")
    // Now you can use `percentage` for whatever you need.
}

Upvotes: 3

Related Questions