Lazy evaluated Coroutines in Kotlin

Async Workflows with Kotlin Pt III

Sampson Oliver
2 min readApr 15, 2018

This is a further extension of my work on Kotlin Coroutines and Promises. If you haven’t seen them, or just aren’t as up to date as you’d like, have a squiz:

So in the past topics, we got async/await functioning, and then built a happy little Promise wrapper around it. Today we’ll show how to lazy-evaluate a piece of asynchronous work, so that we can set up a worker without starting the work during initialisation-time, and later invoke the work during runtime.

Let me give an example, where fetchData is some call that returns either a Deferred<T> or a Promise<T> depending on whether you’re using my optional PromiseLike syntax from Part II of this series.

class MyActivity {  val intentArg by lazy { intent.getExtra(...) }
val dataPromise by lazyDeferred { fetchData(intentArg) }
... fun onCreate() {
setContentView(R.layout.content_view)
showLoadingSpinner() // Using async/await syntax from Pt I
myView.bind(dataPromise.await())
// Using Promise-syntax from Pt II
dataPromise.then { response -> myView.bind(response) }
}
}

The requirement here is that fetchData not execute until it is invoked during onCreate. This is important because it depends upon a lazy value in intentArg which will not be present until after the Android system has populated it during its create lifecycle stage.

lazyDeferred as a Delegated Property

To achieve the lazyDeferred syntax demonstrated above, we’ll use Kotlins’s Delegated Properties. To keep it simple though, we’ll just extend from the existing lazy Delegated Property that comes out-of-the-box. This will let us set up a unit of async work ahead of time, but not actually invoke it until we interact with its result later on.

So to do this, there are two things to do. One, is to ensure the Promise only executes its work when it has to, by setting the Coroutine start property to lazy. The second, is to wrap the object in a lazy-initialiser itself.

fun <T> lazyPromise(block: suspend CoroutineScope.() -> T): Lazy<Deferred<T>> {
return lazy {
async(start = CoroutineStart.LAZY) {
block.invoke(this)
}
}
}

And that’s it. Now it’s easier than ever coordinate your asynchronous workloads without hassle, either with Promise-Like syntax or by writing synchronous-looking code.

--

--

Sampson Oliver

Team lead @acloudguru, serverless junky, runner, feminist, & infrequent music producer. @sampsonjoliver on GitHub/twitter et al