Coroutines delay() and cancel()

I made a neverending Loop with delay(time) which i need to end by e.g. a click.

cancel() seems to be the wrong function for this.

What is your use case exactly? You can do this in many different ways, e.g. with some boolean flag, by cancelling, with messages sent through Channel, etc., but it really depends. Do you run some repetitive task that you need to stop? Or maybe you need to pause some task and resume it using an external event?

Well.

As Example:

var time:long = 10000
var loop:Boolean = true
fun exampleEverlastingLoob()=GlobalScope.launch{
      while(loop){
               //dosmoething
               delay(time)
      }
}
exampleEverlastingLoob()

Now i want to change only time by e.g. click from 10000 to 3000. When i do so, i want the function to stop right away and start again whith the new time, othwise it finishes it’s jop with the old 10000 time first before it changes to 3000.

For that reason i need to kill the job first, change the time and than rerun the function right?
And that’s my question. How can i kill this? Even loop=false wont’t do it right away because the job waits for it’s 10000 to be over

For those who care

class Interval (main: MainActivity) {
    var runner: Boolean = true

    var time=main.time
    var job:Job = GlobalScope.launch{

    }

    fun setInterval(function:()->(Unit))= GlobalScope.launch {
        runner=true

       job = launch {

            while (runner) {
                if (!runner) {
                   job. cancel()
                } else {
                    function()
                    println(time)
                    delay(time)
                }
            }
        }
        }



    fun clearInterval()= GlobalScope.launch {
        runner=false

        job.cancel()


    }


}

Works

In that case I think killing/cancelling the loop makes sense. We just need to store Job object returned from GlobalScope.launch(), then invoke cancel() on it and start again.

If for some reason you prefer not to cancel and restart, but to go with the loop continuously, you need to signal/notify the loop externally. We can do this with multiple ways, but the easiest is probably to use Channel. Then, instead of waiting for some time to pass (delay()), we need to wait for either time or external notification, whatever happens first. Again, we can do this in multiple ways, but built-in timeouts should do the trick.

Full example:

suspend fun main() = coroutineScope {
    launch { loop() }

    delay(7000)

    // change interval and notify the loop
    time = 1000
    channel.send(Unit)
}

suspend fun loop() {
    while (true) {
        println("Working...")
        withTimeoutOrNull(time) {
            channel.receive()
        }
    }
}

private var time = 3000L
private val channel = Channel<Unit>()

By sending to the channel we notify the loop to resume. If we don’t do this, it will timeout after time.

Note that both this solution and solution based on restarts, may print “Working…” more frequently than asked. For example, we start the loop with 10s interval and then in 11th second we switch it to 5s interval. The loop will immediately resume, printing “Working…” only 1s after the last one. I don’t know if this is fine for you or not. If not then we need to somehow handle this case as well.

This would be better:

class Interval(private val time: Long) {
    private var job: Job? = null
    
    fun start(function: () -> Unit) {
        job = GlobalScope.launch {
            while (true) {
                function()
                println(time)
                delay(time)
            }
        }
    }
    
    fun stop() {
        job?.cancel()
    }
}

Thanks to both of you