how to use suspendCoroutine to turn java 7 future into kotlin suspending function

Java 7 future is blocking. It is not designed for asynchronous APIs and does not provide any way to install a callback that is invoked when the future is complete. It means that there is no direct way to use suspendCoroutine with it, because suspendCoroutine is designed for use with asynchronous callback-using APIs.

However, if your code is, in fact, running under JDK 8 or a newer version, there are high chances that the actual Future instance that you have in your code happens to implement CompletionStage interface at run-time. You can try to cast it to CompletionStage and use ready-to-use CompletionStage.await extension from kotlinx-coroutines-jdk8 module of kotlinx.coroutines library.


Of course Roman is right that a Java Future does not let you provide a callback for when the work is done.

However, it does give you a way to check if the work is done, and if it is, then calling .get() won't block.

Luckily for us, we also have a cheap way to divert a thread to quickly do a poll check via coroutines.

Let's write that polling logic and also vend it as an extension method:

suspend fun <T> Future<T>.wait(): T {
    while(!isDone)
        delay(1) // or whatever you want your polling frequency to be
    return get()
}

Then to use:

fun someBlockingWork(): Future<String> { ... }

suspend fun useWork() {
    val result = someBlockingWork().wait()
    println("Result: $result")
}

So we have millisecond-response time to our Futures completing without using any extra threads.


And of course you'll want to add some upper bound to use as a timeout so you don't end up waiting forever. In that case, we can update the code just a little:

suspend fun <T> Future<T>.wait(timeoutMs: Int = 60000): T? {
    val start = System.currentTimeMillis()
    while (!isDone) {
        if (System.currentTimeMillis() - start > timeoutMs)
            return null
        delay(1)
    }
    return get()
}