Skip to content

Instantly share code, notes, and snippets.

@fluidsonic
Created April 20, 2020 13:59
Show Gist options
  • Save fluidsonic/be1cf0329ecc1994cf911beefdcb0631 to your computer and use it in GitHub Desktop.
Save fluidsonic/be1cf0329ecc1994cf911beefdcb0631 to your computer and use it in GitHub Desktop.
@file:OptIn(ExperimentalTime::class)
import kotlinx.coroutines.*
import kotlin.time.*
suspend fun main() {
coroutineScope {
println("Launching tasks…")
val printTask = PrintDelayedTask.launchIn(this, delay = 5.seconds, text = "PrintDelayedTask prints!")
val validationTask = ValidationTask.launchIn(this) { isValid ->
println("Validation task completed. isValid = $isValid")
}
println("Tasks launched!")
delay(6.seconds)
printTask.cancel()
println("Waiting for current validation try…")
validationTask.waitForCurrentTry()
println("Waiting for all tasks to complete…")
}
println("All done!")
}
interface Task {
fun cancel()
}
abstract class AbstractTask(scope: CoroutineScope) : Task {
protected val job: Job
init {
// probably a bad idea as the subclass may not be fully initialized yet
job = scope.launch { run() }
}
override fun cancel() {
job.cancel()
}
protected abstract suspend fun run()
}
class PrintDelayedTask private constructor(
scope: CoroutineScope,
private val delay: Duration,
private val text: String
) : AbstractTask(scope) {
override suspend fun run() {
delay(delay)
println(text)
}
companion object {
fun launchIn(
scope: CoroutineScope,
delay: Duration,
text: String
) =
PrintDelayedTask(scope = scope, delay = delay, text = text)
}
}
class ValidationTask private constructor(
scope: CoroutineScope,
private val completion: (isValid: Boolean) -> Unit
) : AbstractTask(scope) {
private var currentTryJob = Job()
override suspend fun run() {
var isValid: Boolean
var tryNumber = 0
while (true) {
tryNumber += 1
try {
isValid = someValidation(tryNumber)
break
} catch (e: Exception) {
// oh no!
}
println("Validation try $tryNumber failed. Retrying…")
currentTryJob.complete()
currentTryJob = Job()
delay(6.seconds)
}
currentTryJob.complete()
if (!job.isCancelled)
completion(isValid)
}
suspend fun waitForCurrentTry() {
currentTryJob.join()
}
companion object {
fun launchIn(
scope: CoroutineScope,
completion: (isValid: Boolean) -> Unit
) =
ValidationTask(scope = scope, completion = completion)
}
}
suspend fun someValidation(tryNumber: Int): Boolean {
delay(1.seconds)
if (tryNumber < 3)
error("Nice try!")
return true
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment