Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Repeat a task within a duration with delay

I have to move a progress bar within a time frame, for example within 6s. I am using coroutines and the "repeat" function. The code executes except that the total execution time is not as specified. Below is my code.

val progressJob = Job()
var startTime = 0L
CoroutineScope(Dispatchers.Default + progressJob).launch {
    startTime = System.currentTimeMillis()
    repeat(1000) {
        progressBar.progress += 1
        delay(6)
    }
    Log.d(TAG, "total time= ${System.currentTimeMillis() - startTime}")
}

I am expecting "total time" would be 6000, but I am getting values greater than 6000 by at least 500.

Basically I just want to repeatedly increment the progress bar within a time frame, and I am not using animation because of performance issue.

Is there anything I am missing?

like image 410
Hayton Leung Avatar asked Apr 10 '19 07:04

Hayton Leung


2 Answers

I would do it with something like this:

withTimeout(1300L) {
    repeat(1000) { i ->
        println("Blip Blop $i ...")
        delay(500L)
    }
}

For more examples see official doc: https://kotlinlang.org/docs/reference/coroutines/cancellation-and-timeouts.html

like image 154
Javatar Avatar answered Oct 08 '22 10:10

Javatar


so what you are doing here is simulating progress. Ideally, there would be some way of checking the actual progress of your bar, and updating it, and when it is done, ending. But, if this is not possible, then ya, simulation is your choice.

So, with coroutines we are dealing with a threaded environment, and within that, we have our coroutines which need to be continued when the hand over control of execution. In your implementation, this happens at the delay call. For this reason, it is very difficult to guarantee that your coroutine will complete in your desired time. All delay can do is say that it will not resume before "at least" the specified time has elapsed, and probably quite often, more time would have elapsed, not the exact time.

So, how do we get this to execute in as close to your desired time frame as possible? What we need to do is drop the repeat, and rather check on the elapsed time to decide if we finish. Here is a rough implementation that will hopefully help.

class Bar(val barLength: Int = 1000) {
    var progress = 0
}

suspend fun simulateProgress(bar: Bar, job: Job, totalDurationMillis: Long, incrementsMills: Long): Job {
    var startTime = System.currentTimeMillis()
    return CoroutineScope(Dispatchers.Default + job).launch {
        var totalElapsed = 0L
        while (totalElapsed < totalDurationMillis) {
            totalElapsed = System.currentTimeMillis() - startTime
            val progressRatio = totalElapsed.toDouble()/totalDurationMillis.toDouble()
            bar.progress = (progressRatio * bar.barLength.toDouble()).toInt()
            delay(incrementsMills)
        }
        println("Elapsed: $totalElapsed, Progress: ${bar.progress}")
    }
}

fun main() = runBlocking {
    val job = Job()
    val bar = Bar()
    val progressJob = simulateProgress(bar, job, 6000, 10)
    progressJob.join()
} 
like image 41
Laurence Avatar answered Oct 08 '22 10:10

Laurence