Szymon Jeziorski
02/07/2023, 1:11 PMexpiresAt
,refreshAllowedAfter
and business payload within it. It doesn't make sense to try refreshing it before refreshAllowedAfter
and I would like to initiate some action when token expires and is not correctly refreshed in time, for example notify some external service about this fact. The simplest and naive solution would be to use Java Timers, but I would like to leverage coroutines as creating new thread per each operation and managing it sound like an overkill for me.
Below is something I prototyped, but am not sure about the pitfalls of this approach as I haven't had similar use cases yet.
What do you think about it? Would it be fine to use something like this or do you see some red flags straight away with a better alternative in mind? Thanks in advance.
private val timerScope = Executors.newSingleThreadExecutor()
.asCoroutineDispatcher()
.let { dispatcher -> CoroutineScope(dispatcher + SupervisorJob()) }
class Weather(private val weatherApiClient: WheaterApiClient) {
var weatherApiToken: WeatherApiToken? = null
private set
private var tokenRefreshJob: Job? by Delegates.observable(null) { _, oldJob, _ -> oldJob?.cancel() }
private var weatherDataExpiredNotifyJob: Job? by Delegates.observable(null) { _, oldJob, _ -> oldJob?.cancel() }
fun initiateWeatherToken() {
...
scheduleWeatherTokenRefresh()
}
private fun scheduleWeatherTokenRefresh() {
tokenRefreshJob = timerScope.launch {
while (isActive) {
runCatching {
val newToken = weatherApiClient.getWeatherDataToken()
weatherApiToken = newToken
weatherDataExpiredNotifyJob = timerScope.launch { notifyAboutWeatherTokenExpiry(newToken) }
delay(newToken.refreshAllowedAfter.millisFromNow())
}.onFailure { [onFailureCallback] }
}
}
}
private suspend fun notifyAboutWeatherTokenExpiry(token: WeatherApiToken) {
delay(token.expiresAt.millisFromNow())
[some logic]
}
}
ephemient
02/07/2023, 1:34 PMsuspend fun pollWeatherToken() {
flow {
while (true) {
val newToken = ...
emit(newToken)
delay(newToken.refreshAllowedAfter.millisFromNow())
}
}
.collectLatest { token ->
delay(token.expiresAt.millisFromNow())
[some logic]
}
}
where the collectLatest
coroutine gets automatically cancelled every time there is a new upstream emission