natschz
natschz

Reputation: 1117

How To await a function call?

So I have some asynchronous operations happening, I can create some lambada, call a function and pass that value to them. But what i want is not to have the result of the operation as a parameter, I want to return them.

As a example, I have a class A with some listeners, if there is a result all listeners are notified. So basically the asyncFunction should return a result if there is one otherwise be suspended.

object A {
    val listeners = mutableListOf<(Int) -> Unit>()

    fun onResult(value: Int) {
        listeners.forEach { it(value) }
    }
}

fun asyncFunction(): Deferred<Int> {
    return async {
        A.listeners.add({ result ->

        })

        return result
    }
}

What I'm thinking right now (maybe I'm completely on the wrong track), is to have something like a Deferred, to which i can send the result and it returns. Is there something like that? Can I implement a Deffered myself?

class A {
    private val awaiter: ??? // can this be a Deferred ?

    fun onResult(result: Int) {
        awaiter.putResult(result)
    }

    fun awaitResult(): Int  {
        return awaiter.await()
    }
}

val a = A()

launch {
    val result = a.awaitResult()
}

launch {
    a.onResult(42)
}

So I do know that with callbacks this can be handled but it would be cleaner and easier to have it that way.

I hope there is a nice and clean solution im just missing.

Upvotes: 1

Views: 226

Answers (1)

Marko Topolnik
Marko Topolnik

Reputation: 200256

Your asyncFunction should in fact be a suspendable function:

suspend fun suspendFunction(): Int =
    suspendCoroutine { cont -> A.listeners.add { cont.resume(it) } }

Note that it returns the Int result and suspends until it's available.

However, this is just a fix for your immediate problem. It will still malfunction in many ways:

  • the listener's purpose is served as soon as it gets the first result, but it stays in the listener list forever, resulting in a memory leak
  • if the result arrived before you called suspendFunction, it will miss it and hang.

You can keep improving it manually (it's a good way to learn) or switch to a solid solution provided by the standard library. The library solution is CompletableDeferred:

object A {
    val result = CompletableDeferred<Int>()

    fun provideResult(r: Int) {
        result.complete(r)
    }
}

suspend fun suspendFunction(): Int = A.result.await()

Upvotes: 3

Related Questions