Issue
I have a scenario where my code has to send an api call and move on with its work (which contains another api call) without waiting for the result of the first call.
Right now I do this in my viewmodel
fun showItem(id:Int) {
launch{
repo.markItemRead(id)
}
launch {
try {
val item = repo.getItemById(id).getOrThrow
commands.postValue(ShowItemCommand(item))
} catch (t:Throwable) {
commands.postValue(ShowError(R.string.error_retrieve_item))
repo.logError(t)
}
}
}
this calls the repository which has these two functions
suspend fun markItemRead(id) {
try {
service.markItemAsRead(id)
} catch(ignored:Throwable) {
}
}
suspend fun getItemById(id) : Result<ItemData> {
return try {
val response : ItemEntity = service.getItemById(id)
val item = response.toData()
Result.Success(item)
} catch (t:Throwable) {
Result.Failure(t)
}
}
I would prefer it if the repository did all those jobs because one has to follow the other every time.
Unfortunatelly when I try to do something like this in my repository:
suspend fun getItemById(id:Int) : Result<ItemData> {
try {
service.markItemAsRead(id)
} catch(ignored:Throwable) {
}
return try {
val response : ItemEntity = service.getItemById(id)
val item = response.toData()
Result.Success(item)
} catch (t:Throwable) {
Result.Failure(t)
}
}
It waits for the markItemAsRead
function to finish before moving on
Other than defining a scope for the repository and putting the markItemAsRead
call inside a launch
(which I have read is incorrect to do inside a suspending function) is there another way of doing this inside the repository?
Solution
You can use coroutineScope
or supervisorScope
in the repository, depending on your needs. Both functions are designed for parallel decomposition of work. These functions return as soon as the given block and all its children coroutines are completed.
When any child coroutine in coroutineScope
fails, this scope fails and all the rest of the children are cancelled. Unlike coroutineScope
, a failure of a child coroutine in supervisorScope
does not cause this scope to fail and does not affect its other children, so a custom policy for handling failures of its children can be implemented.
Please choose what best suits your needs. Example of usage:
suspend fun getItemByIdAndMarkRead(id: Int) : Result<ItemData> = supervisorScope {
launch {
try {
service.markItemAsRead(id)
} catch(ignored:Throwable) { }
}
return@supervisorScope withContext(Dispatchers.Default) {
try {
val response : ItemEntity = service.getItemById(id)
val item = response.toData()
Result.Success(item)
} catch (t: Throwable) {
Result.Failure(t)
}
}
}
service.markItemAsRead(id)
and service.getItemById(id)
will execute in parallel.
Answered By - Sergey
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.