Akshay Shah
01/27/2021, 5:50 PMDavid Hernando
01/27/2021, 5:55 PMPaul Griffith
01/27/2021, 5:56 PMAkshay Shah
01/27/2021, 5:59 PMPaul Griffith
01/27/2021, 6:19 PMAlex Nordlund
01/27/2021, 9:04 PMJoris PZ
01/28/2021, 8:10 AMdelay
, such as
fun CoroutineScope.runEvery(
duration: Duration,
initialDelay: Duration = Duration.ZERO,
action: suspend () -> Unit
) = launch(start = CoroutineStart.UNDISPATCHED) {
delay(initialDelay.toMillis())
while (true) {
try {
action()
} catch (e: Exception) {
logger.error(e) { "Task repeated every $duration threw exception" }
}
delay(duration.toMillis())
}
}
Like @Alex Nordlund says, this does not correct for drift, so we only use it for cases where we want to run stuff say roughly every hour.
To run stuff daily at or just after a certain time, we use something like
fun CoroutineScope.runDailyAt(runAt: LocalTime, action: suspend () -> Unit) =
launch(start = CoroutineStart.UNDISPATCHED) {
while (true) {
val now = TimeService.now()
val planned = runAt.atDate(now.toLocalDate()).atZone(now.zone).let { planned ->
if (now.isBefore(planned)) planned else planned.plusDays(1)
}
logger.debug { "Scheduling next daily task at $planned" }
delay(Duration.between(now, planned).toMillis())
try {
action()
} catch (e: Exception) {
logger.error(e) { "Daily task threw exception" }
}
}
}
And we have a few versions parsing cron schedules etcetera but the basic idea is the same. It's simple but so far it works really well.Alex Nordlund
01/28/2021, 8:21 AMRobert MacLean
01/28/2021, 9:25 AM