2

I have a kotlin code like following.

private val faceProcessorFlow = MutableSharedFlow<Pair<EnrollmentModel.Waiting, Int>>()
    
init {
    viewModelScope.launch(IO) {
         faceProcessorFlow
              .onEach {
                    performEnrollment(it)
               }.collect()
    }
}
    
fun start(){
    itemsToDetect.forEach { (index, itemToDetect) ->
         viewModelScope.launch(IO) {
             val item = (itemToDetect as? EnrollmentModel.Waiting)
             if (item != null)
                  faceProcessorFlow.emit(item to index)
             else
                  updateUI()
          }
    }
}

Once the start method is called, it takes pretty long time. Well, it depends on the size of itemsToDetect. I just want to stop and resume this task when it needs. I have been searching similar solutions but they are not proper on my case, so I ask this question with detailed code. Please help me. Please share your idea with me.

  • 1
    It's not exactly clear what your intended behavior is. Right now, you are starting parallel coroutines for every item in the `itemsToDetect` synchronously, so there's no place to "stop the task" because it's a bunch of parallel tasks that are already on their last step (either emitting to the shared flow or updating UI). Your use of SharedFlow here looks very unusual. It has no replay or buffer, yet you are emitting many items from a collection to it, so many intermediate values can be expected to never be seen by any collectors of the shared flow. – Tenfour04 Aug 18 '23 at 14:45
  • Thank you very much for your reply. You mean it's impossible to stop and resume coroutine in current code? Then how can I change the code to make it possible? – Shiro Ennosuke Aug 18 '23 at 15:05
  • It is possible to suspend and resume, but when do you want to do this exactly? And how pausing and resuming would fix your problem of computing a big task? – broot Aug 18 '23 at 15:12
  • It's not really clear, yet perhaps [How to suspend kotlin coroutine until notified](https://stackoverflow.com/q/55421710/6395627) is what you're looking for. But your code is confusing. As noted, `MutableStateFlow` can drop intermediate values, meaning `performEnrollment` might be called only for a subset of `itemsToDetect`. Plus, pausing the task will only make the task run longer. – Slaw Aug 18 '23 at 15:21
  • The `performEnrollment` method handles input data and writes it in database. And user can see the progress. When user click button, I just wanna stop/resume progress. – Shiro Ennosuke Aug 18 '23 at 15:25
  • 2
    But what is progress if you already started every discrete part of the task in parallel? There's no place to stop it. This isn't just a coroutine limitation. You can only stop doing something in between discrete steps, but you aren't doing anything sequentially. – Tenfour04 Aug 18 '23 at 16:55
  • Anyway, what's the point of launching all these concurrent coroutines in `forEach {}`? They don't seem to do anything but emitting to the flow. Why not do this without launching new coroutines? If your real work is happening in `performEnrollment()`, then I think you should use `Channel` instead of a flow and you can simply send all items in a loop, without launching coroutines. Then it makes sense to temporarily pause executing `performEnrollment()` for new items - if this is what you need. – broot Aug 18 '23 at 18:33
  • I agree your opinions and changed the code. Simply, exchanged the places of loop and coroutine. – Shiro Ennosuke Aug 21 '23 at 19:06

0 Answers0