You right, in that case, there is a chance of race conditions and that userItem
or payItem
will be in the incorrect case, since there are not concurrency oriented points. The mechanism itself will work, but the corrected state is not guaranteed.
If you need to place mappers in the separate thread, you can do this for the case, when you need to show content, when everything is mapped
private fun mapUser(user: User) {
val newUserItem = ioContext.execute { mapper.mapUser(user) }
val newPayItem = ioContext.execute { mapper.mapPay(user.defaultPayment) }
userItem = newUserItem
payItem = newPayItem
}
Or:
private fun mapUser(user: User) {
val mapUserJob = ioContext.async { mapper.mapUser(user) }
val mapPayJob = ioContext.async { mapper.mapPay(user.defaultPayment) } //or in reverse if you consider that payJob will be faster, so its result will be shown first
userItem = mapUserJob.await()
payItem = mapPayJob.await()
}
If you want to use global loading for that, then you can just change the ordering in the refreshData
to:
ioContext.execute { userService.takeCurrentUser() }
.onSuccess(::mapUser)
.onFailure { view?.showLoadingError(it) }
.also { view?.toggleLoading(false) }
Bonus point: you can keep in some cases progress hiding on the original line and manage small individual progresses for each block in the mapUser
method, since for each of them you have separate coroutine :)