CLOVIS
09/20/2021, 7:34 PMmkrussel
09/20/2021, 7:46 PMasync
with CoroutineStart.LAZY
. Then you can have a suspending function that waits on the deferred that is returned.
Unfortunately this ties the lifecycle of that Deferred
to some scope that may not be related to the scope that is using the results. Depending on what you are doing that might be a problem.
https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-start/index.htmlprivate val httpClient = scope.async(start = CoroutineStart.LAZY) {
val cacheDirectory = options.createCacheDirectory(cacheRootProvider)
val cacheSize = options.cacheSize ?: cacheDirectory?.calculateRecommendedCacheSize() ?: 0L
createOkHttpClient(cacheSize, cacheDirectory)
}
override suspend fun get(requestSettings: HttpRequestSettings): HttpResult<HttpResponse> {
@Suppress("BlockingMethodInNonBlockingContext") // Suspend is there for engines that support async io
return try {
HttpSuccess(OkHttpResponse(httpClient.await().newCall(Request.Builder().apply {
url(requestSettings.url)
for (header in requestSettings.headers) {
header(header.first, header.second)
}
}.build()).execute()))
} catch (e: IOException) {
HttpIoFailure(e)
}
}
Rick Busarow
09/20/2021, 8:03 PMDeferred
which just delegates everything but join()
and await()
to an internal CompletableDeferred
. Then since await()
and join()
are suspend functions, use them to perform the work once and complete
the delegate.
inline fun <reified T> lazyDeferred(crossinline action: suspend () -> T): Deferred<T> {
val delegate = CompletableDeferred<T>()
val lock = Mutex()
return object : Deferred<T> by delegate {
override suspend fun join() {
lock.withLock {
if (!delegate.isCompleted) {
delegate.complete(action())
}
}
return delegate.join()
}
override suspend fun await(): T {
lock.withLock {
if (!delegate.isCompleted) {
delegate.complete(action())
}
}
return delegate.await()
}
}
}
usage:
val myExpensiveThing: Deferred<ExpensiveThing> = lazyDeferred { createExpensiveThing() }
uli
09/20/2021, 8:21 PMby lazy
?
You can then await your deferred over and over again.
val myExpensiveThing: Deferred<ExpensiveThing> by lazy { myScope.async { createExpensiveThing() } }
(Written on mobile. Typos and syntax errors expected)Joffrey
09/21/2021, 11:56 AMval myExpensiveThing = myScope.async(start = CoroutineStart.LAZY) { createExpensiveThing() }
uli
09/21/2021, 1:05 PMby lazy
will not only postpone the execution of the coroutine but also the construction. No access, no coroutine.
On the other hand each getter will go through another indirection. Both probably not detectable. So more a matter of taste.Joffrey
09/21/2021, 1:08 PMlazy
avoids the coroutine construction but adds a delegate construction, so I don't believe we're saving much here. However we do add some indirection. But yeah as you said not much difference, I just find the direct async
call simpler.