https://kotlinlang.org logo
Title
c

Colton Idle

08/07/2022, 5:14 PM
So I know coroutine cancellation is cooperative, but I have a fairly simple case here that I'm not sure how to dig myself out of. I essentially have a button that calls
fetchMoreData()
fun fetchMoreData(i) {
  viewModelScope.launch { ...
but now if I call fetchMoreData() again, I actually want to cancel the old data fetching. How would I do that?
This is the closest thing I've been able to find, but still seems a bit lengthy and at 2 years old, I'm wondering what the idiomatic way to do this is. https://stackoverflow.com/a/63776757
e

ephemient

08/07/2022, 5:18 PM
that answer is a bad idea; that job is not parented to the viewModelScope
c

Chrimaeon

08/07/2022, 5:20 PM
Save the Job returned from the
launch
and cancel it before launching.
e

ephemient

08/07/2022, 5:20 PM
right, that's an option:
var fetchJob: Job? = null
fun fetch() {
    fetchJob?.cancel()
    fetchJob = viewModelScope.launch { ... }
c

Colton Idle

08/07/2022, 5:22 PM
Nice. I was hoping for something easy peasy... but I didn't know if this kinda felt like an rx-ism and so far everything ive ever brought from rx to coroutines has been wrong. hence I wanted to check.
e

ephemient

08/07/2022, 5:24 PM
you could also a flow as a trigger, e.g.
val fetchRequests = MutableSharedFlow<Unit>()
fun start() {
    fetchRequests.mapLatest { ... }.launchIn(viewModelScope)
}
fun fetch() {
    fetchRequests.tryEmit(Unit)
}
c

Colton Idle

08/07/2022, 5:24 PM
@ephemient for the sake of curiousotity. "right, that's an option:" whats some other options that come to mind. To me another option (which goes against the whole cancelling thing) would just be to treat it as a flow/queue and just finish off the current task, and then handle the next.
thanks @ephemient looks like i was on the sort of same page
ah. but yeah. a flowable with *latest would just cancel the previoius.
man. flows/coroutines are so powerful. lol
f

Francesc

08/08/2022, 5:14 AM
Note that it's a convention for a function that launches a coroutine and returns immediately to be an extension on CoroutineScope.
If you need to launch a coroutine that keeps running after your function returns, then make your function an extension of
CoroutineScope
or pass
scope: CoroutineScope
as parameter to make your intent clear in your function signature. Do not make these functions suspending:
https://elizarov.medium.com/coroutine-context-and-scope-c8b255d59055
c

Colton Idle

08/08/2022, 6:11 AM
ooooh. i will read up on this. TIL