Tian Tian098
03/21/2025, 5:23 PMval myResource: Deferred<Closeable> = coroutineScope.async(LAZY) { ... }
What is the best way to close that resource if it has been created? So far I have
fun Deferred<Closeable>.close(coroutineScope: CoroutineScope) {
when {
isActive || isCompleted -> coroutineScope.launch { await().close() }
else -> cancel()
}
}
That seems really complicated for what I'm trying to do thoughTian Tian098
03/21/2025, 5:30 PMisActive
and isCompleted
properly, and maybe if there is some standard-library function I missedkevin.cianfarini
03/21/2025, 5:31 PMsuspend fun Deferred<Closeable>.close() {
await().close()
}
Tian Tian098
03/21/2025, 5:33 PMawait()
will create it just for it to be closed...kevin.cianfarini
03/21/2025, 5:40 PMfun Deferred<Closeable>.close() {
val closeable = try {
getCompleted()
} catch (e: IllegalStateException) {
// Suppress.
null
}
closeable?.close()
}
kevin.cianfarini
03/21/2025, 5:41 PMTian Tian098
03/21/2025, 5:46 PMephemient
03/21/2025, 7:58 PMmyResource.invokeOnCompletion { e ->
if (e == null) cleanup()
but LAZY is sorta tricky for other reasons: even if it's never started, its existence prolongs the parent scope/jobviluon
03/22/2025, 8:58 AMflow {
resource.use {
emit(it)
awaitCompletion()
}
}
Now you have a cold flow that won't open the resource until you start collecting it (change resource
to an expression that opens a file, for example). But the resource will only be closed after you're done working with it in downstream flow operators. You can work with the resource in collect
, or change it with map
and eventually ask for first()
if you need the result outside the flow.
To turn that into a Deferred
, you can eventually do async { resourceFlow.first() }
, but note that the resource will already be closed by the time you await
this expression. Therefore, stateful transformations are a better fit for the flow operators, if you can live with the fact that only the result of all the operations is Deferred
.viluon
03/22/2025, 9:01 AMDeferred<Closeable>
into this idiom by awaiting it in the flow {}
block.Tian Tian098
03/23/2025, 4:00 PMval myResource = lazy { coroutineScope.async { ... } }
This turns out to be faster because it creates the coroutine lazily instead of async(LAZY) { ... }
which creates the coroutine eagerly and just starts it lazily.
The corresponding close function looks like
fun Lazy<Deferred<Closeable>>.close(coroutineScope: CoroutineScope) {
if (isInitialized()) coroutineScope.launch {
value.await().close()
}
}