I have two operations that are done using corroutines. Fetching from a preferences Datastore
, and making an api (retrofit) call
. The network call is dependent on knowing what type of Unit the user prefers (Fahrenheit, Celsius), so I need to get this data first before launching the network call.
I am not sure if I am creating a race condition here or if calling the suspend function like shown below is always going to follow the correct call order.
private suspend fun getUnits(): Units {
var openWeatherUnits: Units = Units.FAHRENHEIT // default
// Get Units
dataStore.readStringDataStore(UNITS_KEY).first().let { units ->
if (units != null) {
println("units: $units")
openWeatherUnits = Units.valueOf(units)
}
}
return openWeatherUnits
}
private fun getWeatherData() {
viewModelScope.launch(Dispatchers.IO) {
val units = getUnits()
// Make API call
val response = apiService.getCurrentWeather(
BuildConfig.OPEN_WEATHER_KEY,
units.value
)
}
}
4
When you call the funstion getWeatherData() it launches a coroutine in viewModelScope. Internally getUnits(), containing another coroutine, is called so there are two coroutines right now ‘getWeatherData’ and ‘getUnits’.
Here the statement ‘val units = getUnits()’ will suspend ‘getWeatherData’ execution until ‘getUnits’ result is received. Once received it will go ahead.
So no race conditions here.
1
Your code will work perfectly fine, Api call will be made after the call getUnits() returns. But if you want to know how actually the problem can be generated here. I give you the example
// Note: Avoid this type of coding,
// this will launch two coroutines running at the same time.
// Here your API can be called even before getUnits() finishes.
fun getWeatherData() {
var units: Units = Units.FAHRENHEIT // default
// getUnits() function call here
viewModelScope.launch(Dispatchers.IO) {
units = getUnits()
}
// Api call here, it will not wait for the above code to be finished
viewModelScope.launch(Dispatchers.IO) {
val response = apiService.getCurrentWeather(
BuildConfig.OPEN_WEATHER_KEY,
units.value
)
}
}
calling suspend functions in different coroutines can create the problem in your case. but here you are right.
In sort, If you have 3 different suspend function and you want to execute them parallel your code could be
viewModelScope.launch(Dispatchers.IO) { fun1() }
viewModelScope.launch(Dispatchers.IO) { fun2() }
viewModelScope.launch(Dispatchers.IO) { fun3() }
this is helpful if you want to call three different API at the same time which are total independent from each other,
But if you want call the suspend function one after other, you can write something like this. e.g. if you want to call fun2() first then fun3() and fun1() at last
viewModelScope.launch(Dispatchers.IO) {
fun2()
fun3() // will be called after fun2() finished
fun1() // will be called after fun1() finished
}
Note: You are right with your code, my code is only for information. do not consider this as the solution of your question
2