is there anything wrong with doing cleanup like th...
# coroutines
l
is there anything wrong with doing cleanup like this in a flow builder block?
Copy code
flow {
  for (i in collection) {
    try {
      emit(i)
    } finally {
      doCleanupOn(i)
    }
  }
}
is there a better way to accomplish this?
o
I'd say that this is the preferred way to do it
l
cool. and this will have the semantics that the suspension will only continue once whatever downstream of this call to emit is completed?
or put another way, when does the suspension from emit continue
r
AFAIK, it's not a good practice to have your
emits
between
try-catch
You can use the
.catch
operator to catch any exception from the upstream
If you leave that
try-catch
, you'll be risking funky behaviour like catching an exception thrown by an operator in the downstream, or even thrown by the terminal operator
Then again, you're using
finally
and no
catch
. You don't expect any exceptions?
l
actually thinking through this i don’t want the finally
i think i just want emit then cleanup
r
There's an
onCompletion
operator
And yeah, if you don't expect any trouble there, I guess you could just do the cleanup right after the emission
l
is onCompletion equivalent to that?
oh no, bc that doesn’t give access to the element
which is what i’m lookign for
r
onCompletion
does the same as having your flow's collection inside a
try-finally
l
except it doesn’t have the element in scope
r
Yup
If you won't have access to those elements at the end, then you can't use it
Can´t you just do
Copy code
flow {
  for (i in collection) {
      emit(i)
      doCleanupOn(i)
  }
}
?
l
yeah exactly
that’s what i’m doing
r
Solved then 😛
l
assuming the semantics are what i said above, which is that all downstream operations on the flow complete before it resumes to doCleanupOn
thanks for your help!
r
No problem, happy to do so 😊
c
I think this thread is endemic of a problem with coroutines: they present as a way to solve the worst flow problems of concurrency, but do they? or like most things, do they just mean you have to find new ways to accommodate same?
o
I am not sure what you mean by that. In general they claim to solve the issues of non-sequential-like async programming, but being able to
emit(i)
and then immediately call
doCleanupOn(i)
is very sequential-like, so this seems to be an example of coroutines working well towards their goal
c
But there are still catches, and questions of when and where things will happen.. the means the idea of a deterministic sequence is not really there right?
o
I feel like that's more like an question of Flow in particular, which is separate from coroutines. And the model is fairly simple, downstream flows pass in a FlowCollector, the upstream calls
emit(i)
, and in
emit
, the downstream uses the element, processes it, etc. So clearly exceptions from downstream are thrown from
emit
.
catch
's implementation uses
collect
&
emit
,
collect
throws the errors from upstream generation and the
emit
calls, and special handling around emit figures out if exceptions thrown from
collect
are from up or downstream. It's not very complicated at all.
c
One thing is for sure: the solutions, are towers erected in sand. The architecture patterns from 2 years ago looked good, had good MVVM approach, they are mostly dust now, Compose ushered in a whole new approach. You don’t even have time to learn things before they are mothballed. My problem with Rx and its spawns (have not looked enough at flow yet) is that they obscure logic. The argument in the Meyer Bible OOSC is that exceptions are useful for that reason primarily, is pretty compelling, but here we are talking about how you have to figure exactly how to sequence and then gird for upstream emissions. I hope you ARE right because I do think some flow is necessary, but if it overwhelms the code and the logic is every 3rd line, we’re doomed. Might as well go back to C where they had to have if statements on each SDK call, and then block-based handlers for integer return types…
d
I don't think this is the correct way to do this. The assumption that the element is fully processed after
emit
returns does not apply to some flow operators, namely buffering ones. But it might work in your use case.
👌 1
l
is there anything you would recommend short of threading the item through the whole flow?
d
I dont have an answer.
u
Hmm, but.. doesnt that presume that the subscriber doesnt switch threads?i.e. if he does, then cleanup will run before subscriber's code, which will then act on a closed instance
feels like the db cursor mapping problem from rxjava, and the result of it was you cannot do it in reactive fashion, you need to map and close it synchronously