I’m still not sure about coroutine extensions, doe...
# coroutines
r
I’m still not sure about coroutine extensions, does this look ok to you?
Copy code
suspend fun <T> withMinimumDuration(duration: Long, block: suspend CoroutineScope.() -> T) =
    coroutineScope {
        val delay = async { delay(duration) }
        val task = async { block() }
        delay.join()
        task.await()
    }
g
it looks fine for me
but you don’t need async
r
How would you do it without async?
g
oh, I see
sorry
anyway, in general looks just fine
r
Technically I don’t need 2 async calls I think
g
yeah, you can remove
async
for delay, should be the same
r
I wanted to have it be an extension on
CoroutineScope
like
launch
and
async
but I don’t know. I didn’t even know
coroutineScope
, it was a quickfix to a warning. I’m not sure I want that. Maybe I want the delay even if there’s an error.
I’ll use this to basically show a clear loading state while doing network requests. But I think with this current implementation, if there’s an error immediately in the request, the loading state will just show for some ms
g
your function will suspend until delay or until block() is finished
it looks as correct approach for this use case
r
How about this
Copy code
suspend fun <T> withMinimumDuration(scope: CoroutineScope, duration: Long, block: suspend () -> T): T {
    val delay = scope.async { delay(duration) }
    try {
        val result = block()
        delay.join()
        return result
    } catch (e: Throwable) {
        delay.join()
        throw e
    }
}
g
you don’t need suspend in this case
No, i don’t see any reason to implement it like this
r
Well I just expect a function named this way to always last
duration
, even if there is an error when calling
block
I could use
coroutineScope
with a try-catch inside maybe?
g
yes, sure
r
Oh
What about this one
Copy code
suspend fun <T> withMinimumDuration(duration: Long, block: suspend () -> T) =
    coroutineScope {
        val delay = async { delay(duration) }
        try {
            block()
        } finally {
            delay.join()
        }
    }
g
but this changes original semantics ( withMinimumDuration), . What if
block()
will return faster than delay?
r
Let’s say
duration
is 1000 and
block
is
delay(500)
. I want the call to withMinimumDuration to last 1000ms
g
It should be opposit:
Copy code
val task = async { block() } // start block
delay(duration) // minimal delay
task.await()
r
Same if
block
is
throw Error()
g
yes, but function from previous example violates this contract
r
I don’t see how
g
if block return in
500
than withMinimumDuration will return
so less than minimal 1000 delay
r
But the finally block should get called
g
ah, it’s final
no try catch, more simple semantics, works in the same way,
r
Yeah I didn’t say your solution was not better, just that mine worked too 😛 Thanks, removing the try
g
but probably you need supervisorScope in this case instead of coroutineScope
to avoid delay cancellation
r
Oh right, that’s why I had a try
But with supervisorScope the error is lost isn’t it?
g
no, it will be thrown on
await()
r
Oh, yeah.
Final function
Copy code
suspend fun <T> withMinimumDuration(duration: Long, block: suspend CoroutineScope.() -> T) =
    supervisorScope {
        val task = async(block = block)
        delay(duration)
        task.await()
    }
g
I would write test for it to be sure, but looks fine for me