Colton Idle
12/15/2021, 4:30 AMviewModelScope.launch {
val thing: String = someSuspendFunctionThatReturnsAValue()
aMethodCallThatIsAFlow(thing).collect {
if (it.status == "SUCCESS"){
state.success = true
//should I call job.stop() here?
}
}
}
My problem is that I want the flowable to stop collecting. Can I just save a ref to the call to launch and then call stop()? Or is starting a flowable in a coroutine scope frowned upon in general?Adam Powell
12/15/2021, 4:42 AMaMethodCallThatIsAFlow(thing).first { it.status == "SUCCESSFUL" }
state.success = true
?Colton Idle
12/15/2021, 4:46 AMAdam Powell
12/15/2021, 5:13 AMColton Idle
12/15/2021, 5:21 AMfun confirmEvent(successEvent: () -> Unit) {
viewModelScope.launch {
var response = repository.getIdFromServer()
if (response.isValid) {
var response2 = repository.getObjectWithCurrentStatus(response.data.id)
//Keep listening to changes on the server until the status changes from IN_PROGRESS TO SUCCESS
response2.collect {
if (it.status != "IN_PROGRESS") {
if (it.status == "SUCCESS") {
successEvent()
} else {
state.errorDialog = true
state.errorDialogText = "Error 2: " + it.status
}
}
}
} else {
state.errorDialog = true
state.errorDialogText = response.errorText
}
}
}
first
does not work. But maybe I did it wrong?
fun confirmEvent(successEvent: () -> Unit) {
viewModelScope.launch {
var response = repository.getIdFromServer()
if (response.isValid) {
var response2 = repository.getObjectWithCurrentStatus(response.data.id)
//Keep listening to changes on the server until the status changes from IN_PROGRESS TO SUCCESS
val waiting = response2.first {(it.status == "SUCCESS")}
if (waiting.status != "IN_PROGRESS") {
if (waiting.status == "SUCCESS") {
successEvent()
} else {
state.errorDialog = true
state.errorDialogText = "Error 2: " + waiting.status
}
}
}
} else {
state.errorDialog = true
state.errorDialogText = response.errorText
}
}
}
first
didn't exist and collect
is all I had?viewModelScope.launch {
beaconService.streamTest().collect {
//Do something then
this.coroutineContext.job.cancel()
}
}
Francesc
12/15/2021, 6:28 AMColton Idle
12/15/2021, 6:33 AMthis.coroutineContext.job.cancel()
should work fine then? Because I'm cancelling the job? or should I do
val job = viewModelScope.launch {
beaconService.streamTest().collect {
//Do something then
job.cancel()
}
}
Francesc
12/15/2021, 6:34 AMColton Idle
12/15/2021, 3:03 PMAdam Powell
12/15/2021, 3:38 PMColton Idle
12/15/2021, 3:39 PMreturn
out of the flow, but I guess throw
is what I should use.this.coroutineContext.job.cancel()
did seem to work also.Adam Powell
12/15/2021, 3:43 PMColton Idle
12/15/2021, 3:46 PMthrow
it is.throw
isn't mentioned at all here.
https://stackoverflow.com/questions/57088428/kotlin-flow-how-to-unsubscribe-stopAdam Powell
12/15/2021, 3:47 PMcancel()
is doing is causing other suspend points and cancellation checks later to throw a CancellationException
, and as the inner callee you don't know how broad that job
isflow {
for (item in list) {
emit(item)
}
}
how can something called by emit
cause that for
loop to stop iterating other than by throwing?flow {
val thing = openThing()
try {
while (thing.hasMore()) {
emit(thing.readMore())
}
} finally {
thing.close()
}
}
how do you make this code stop reading this resource before it's finished and properly clean up?Colton Idle
12/15/2021, 3:56 PMAdam Powell
12/15/2021, 3:57 PMColton Idle
12/15/2021, 4:07 PMAdam Powell
12/15/2021, 4:13 PMNick Allen
12/15/2021, 5:14 PMviewModelScope.launch { // Receiver of lambda is CoroutineScope for just this launched Job
this.coroutineContext.job.cancel() //Does not cancel viewModelScope, just cancels the launched Job
cancel() // Or you can just call it on the scope :)
}
first { ... }
isn't enough, takeWhile { ... }
or transformWhile { ... }
may be useful so you don't need to deal with the exceptions yourself.Adam Powell
12/15/2021, 6:49 PMcancel within launch does not cancel the parent scopewhile this is technically correct, cancelling your current scope is very often something you do not want to do. Consider:
suspend fun doAThing() {
someFlow.collect {
if (it % 2 == 0) coroutineContext.job.cancel()
}
}
// ...
doAThing()
doSomethingElse() // does this run? does it finish its work? why or why not?