Home > Blockchain >  How to create a countdown with flow coroutines
How to create a countdown with flow coroutines

Time:11-19

I'm trying to create a flow with coroutines but it's not giving to me the expected result. What I'd like to have is giving an expiration time (doesn't matter if it's in millis, seconds, etc..) when the time arrives to 0 it stops the countdown. What I have now is :

private fun tickerFlow(start: Long, end: Long = 0L) = flow {
        var count = start
        while (count >= end) {
            emit(Unit)
            count--
            delay(1_000L)
        }
    }

And then I call this function as :

val expireDate = LocalDateTime.now().plusSeconds(10L).toEpochSecond(ZoneOffset.UTC)
                tickerFlow(expireDate)
                    .map { LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) - expireDate }
                    .distinctUntilChanged { old, new ->
                        old == new
                    }
                    .onEach {
                        //Here I should print the timer going down with this pattern 
                        //00h: 00m: 00s I did it with String.format("dh: dm: ds") and it works though.
                    }
                    .onCompletion {
                        //Setting the text when completed
                    }
                    .launchIn(scope = scope)

But even with this test that what I'm trying is to have the expiry time as 10 seconds from now it doesn't print nor end as I would. Am I missing something? Is there any way I could emit the local date time so I have the hours, minutes and seconds? perhaps I have to do the calculus to get the seconds, minutes hours from milis / seconds.

TLDR;

I'm getting from backend an expiry date, and I want to know when this expiry date finish so I have to calculate it with the now() and check when it is expired.

CodePudding user response:

You don't really need a flow here. Try this code:

val expireDate = LocalDateTime.now().plusSeconds(10L).toEpochSecond(ZoneOffset.UTC)
val currentTime = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC)
for (i in (expireDate - currentTime)  downTo 1) {
    println("$i seconds remaining") // Format the remaining seconds however you wish
    delay(1_000) // Delay for 1 second
}
println("TIME UP") // Run your completion code here

Also, this code is safe to run on main thread as delay doesn't block.

In your code, the problem is that you are passing the expireDate itself to tickerFlow. expireDate contains the time in seconds from epoch and not the seconds difference from current time. Just pass expireDate - LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) to tickerFlow and it will work.

EDIT: Complete implementation using flow

private fun tickerFlow(start: Long, end: Long = 0L) = flow {
    for (i in start downTo end) {
        emit(i)
        delay(1_000)
    }
}
val expireDate = LocalDateTime.now().plusSeconds(10L).toEpochSecond(ZoneOffset.UTC)
val currentTime = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC)
tickerFlow(expireDate - currentTime)
    .onEach { secondsRemaining ->
        // Format and display the time
    }
    .onCompletion { 
        // Handle completion
    }
    .launchIn(scope)

CodePudding user response:

Solution with flow

private fun countDownFlow(
        start: Long,
        delayInSeconds: Long = 1_000L,
    ) = flow {
        var count = start
        while (count >= 0L) {
            emit(count--)
            delay(delayInSeconds)
        }
    }

And then given an expiration date, get the current date subtract them and pass it as start.

val expireDate = LocalDateTime.now().plusSeconds(10L).toEpochSecond(ZoneOffset.UTC)
val currentTime = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC)
tickerFlow(expireDate - currentTime)
                    .onEach {
                        binding.yourTimer.text = String.format(
                            "dh: dm: ds",
                            TimeUnit.SECONDS.toHours(it),
                            TimeUnit.SECONDS.toMinutes(it),
                            TimeUnit.SECONDS.toSeconds(it),
                        )
                    }
                    .onCompletion {
                        //Update UI
                    }
                    .launchIn(coroutineScope)

  • Related