Kotlin / kotlinx.coroutines

Library support for Kotlin coroutines
Apache License 2.0
13.07k stars 1.85k forks source link

A function to repeat an operation indefintely with a fixed delay. #3680

Open elizarov opened 1 year ago

elizarov commented 1 year ago

Use case

I often see the following pattern in my own code and in the code other people write:

while (true) {
    operation()
    delay(someTimePeriod)
}

This reminds of the repeat function in the standard library that also server for the purpose of codifying the simple loop boilerplate. It looks that this should be a built-in function in the library.

The Shape of the API

While it is clearly possible to over-engineer this function to provide various ways to measure time and do a fixed-interval or fixed-period repetitions, this simple pattern I've shown seems to be the most popular one that needs its own function. I've asked ChatGPT for potential names with this prompt: "Kotlin has repeat function that repeats a given operation a specified number of times. For coroutines, we need a function that repeats a given operation indefinitely often, but has a fixed delay between executions. List potential candidate names for such a function.". Here is the answer it produced:

Here are some potential names for the function:

dkhalanskyjb commented 1 year ago

Character (codegolf) economy seems to be not that big:

while (true)delay
repeatWithDelay

As opposed to repeat, map, fold, or many other things, this doesn't seem to introduce a new level of abstraction with interesting guarantees that simplify understanding code at a glance (like map promises to preserve the structure, repeat guarantees that there are no funky mutable index manipulations, etc.), so I don't understand what would be gained.

Something would, however, be lost.

elizarov commented 1 year ago

I never needed break or anything like that. It typically runs until cancelled. My main gripe with while(true) { ... ; delay(...) } block is that its intent is not clear upfront. Unless the code is clearly structured and the body inside the block is extracted into a separate function, I have to scan to the end of the loop to see that it does a fixed delay between iterations. That is what a clearly named function could solve.

Over time, I've found that I always have to write a helper function to make my code more readable, like this: https://github.com/elizarov/BGP-proxy/blob/main/src/commonMain/kotlin/BgpProxy.kt#L290-L295

JakeWharton commented 1 year ago

It's also worth discussing whether the duration supplied is an interval (between the end of an execution and the start of the next) or a period (between the starts of successive operations). And if it's the latter is there a policy for long tasks to cancel the previous op to run the new one, run the new one ASAP, or skip until next scheduled time. Also if it's a period is the measure done with wall clock time (ignores CPU sleep) or CPU time (susceptible to sleep).

An interval is the obvious choice, but in that case I agree the while (true) syntax is probably enough already. It's only the period case that starts to require non-trivial amount of code that one would want to abstract.

dovchinnikov commented 1 year ago
dovchinnikov commented 1 year ago

Related #1632

dkhalanskyjb commented 1 year ago

From the initial submission:

this simple pattern I've shown seems to be the most popular one that needs its own function

Here's a thought: what if this pattern is the most popular one because it is simple, when actually, people want fixed-period execution, but they figure that this loop is good enough for their (typically fast) actions? What are the specific use cases for fixed-quiescence execution?

elizarov commented 1 year ago

The great thing about fixed delay between executions is that it is self-balancing under a load. The longer each operation takes, the longer period between calls becomes. It is not much useful for real-time stuff, like animations, but I'd say it is the approach of choice for any kind of "update this value periodically" type of application.

mcpiroman commented 1 year ago

when actually, people want fixed-period execution, but they figure that this loop is good enough for their (typically fast) actions?

It actually happens to me. Also note: setInterval is very popular in JS.

So I'd like to have the over-engineered function that provides various ways to measure time and do a fixed-interval or fixed-period repetition in coroutines library, possibly also with a parameter to behave in the fixed-delay manner. But to have a function just for that seems like a little overkill where while(true) is enough.

While loop is also advantageous in that, besides break, one can write (and immediately see) the delay function either at the beginning, end of middle of the loop, or have multiple or even conditional delays.