Search code examples
kotlinasync-awaitkotlin-coroutinessuspend

In Kotlin, is it possible to substitute a suspend fun with a non-suspend version, without breaking the caller?


I'm learning concurrency in Kotlin, coming from C#/JavaScript background, and I can't help comparing some concepts.

In C# and JavaScript, technically we can rewrite an async function as a regular non-async version doing the same thing, using Task.ContinueWith or Promise.then etc.

The caller of the function wouldn't even notice the difference (I ranted about it in a blog post).

Is something like that possible for a suspend function in Kotlin (i.e., without changing the calling code)? I don't think it is, but I thought I'd still ask.

The closest thing I could come up with is below (Kotlin playground link), I still have to call .await():

import kotlinx.coroutines.*

suspend fun suspendableDelay(ms: Long): Long {
    delay(ms);
    return ms;
}

fun regularDelay(ms: Long): Deferred<Long> {
    val d = CompletableDeferred<Long>()
    GlobalScope.async { delay(ms); d.complete(ms) }
    return d;
}

suspend fun test(ms: Long): Long {
    delay(ms);
    return ms;
}

fun main() {
    val r1 = runBlocking { suspendableDelay(250) }
    println("suspendableDelay ended: $r1");
    val r2 = runBlocking { regularDelay(500).await() }
    println("regularDelay ended: $r2");
}

https://pl.kotl.in/_AmzanwcB


Solution

  • If you're on JVM 8 or higher, you can make a function that calls the suspend function in an async job and returns a CompletableFuture, which can be used to get your result with a callback (thenApplyAsync()) or synchronously (get()).

    val scope = CoroutineScope(SupervisorJob())
    
    suspend fun foo(): Int {
        delay(500)
        return Random.nextInt(10)
    }
    
    fun fooAsync(): CompletableFuture<Int> = scope.async { foo() }.asCompletableFuture()
    
    fun main() {
        fooAsync()
            .thenApplyAsync { println(it) }
        Thread.sleep(1000)
    }
    

    The above requires the kotlinx-coroutines-jdk8 library.

    I don't know of a solution that works across multiple platforms.