Home > OS >  How to return a result of high-order function from inner function
How to return a result of high-order function from inner function

Time:12-20

I have a "high-order" function that have to return some value. Inside that "high-order" function there is an "inside" function which really is a producer of the return value of a "high-order" function.

It is simpler to show it with an example what I mean:

lifecycle.coroutineScope.launch {
        val result = doWork() 
        Log.d("Tag", "some result: ${result.someString}")
    }

private val service = SomeService()

suspend fun doWork(): DoWorkResult {

    fun onSomeString(someString: String): DoWorkResult {

        //some execution

        val returnResultForDoWork = DoWorkResult(someString)

        //How to return 'returnResultForDoWork' from fun doWork
        return returnResultForDoWork
    }

    service.getString { someString ->
        onSomeString(someString)
    }

}

class SomeService() {
    suspend fun getString(
        onResult: (String) -> Unit
    ) {
        delay(1000)
        onResult("work is done")
    }
}

data class DoWorkResult(val someString: String)

flow execution:

  1. call service.getString
  2. call onSomeString(someString) when a someString is return from service.getString
  3. in onSomeString analyse/handle a someString and return (how?) a DoWorkResult(someString) from doWork

My question is how to return a result of an onSomeString function as a result of a doWork function?

CodePudding user response:

Suspend functions don't need higher order callbacks like that. Really, it's an antipattern, because it restores back "callback hell" that coroutines solve. A proper version of your function would look like:

class SomeService() {
    suspend fun getString(): String {
        delay(1000)
        return "work is done"
    }
}

And then your calling function becomes:

suspend fun doWork(): DoWorkResult {
    val serviceReturnValue = getString()

    //some execution

    val returnResultForDoWork = DoWorkResult(serviceReturnValue)
    return returnResultForDoWork
}

But let's suppose your service function is not a suspend function, but rather it is asynchronous with a callback, and you don't have control over the source code to make it a suspend function instead.

class SomeService() {
    fun getString(
        onResult: (String) -> Unit
    ) {
        val handler = Handler(Looper.myLooper())
        thread {
            Thread.sleep(1000) //simulate some work
            handler.post { onResult("work is done") }
        }
    }
}

Then to be able to return the callback's inner value in a suspend function, you need to convert the asynchronous function into a suspending one. This can be done with suspendCoroutine or suspendCancellableCoroutine. There are many examples you can look up on this site or online, but here's a quick sample. You can write it as an extension function to work like an overloaded version of the asynchronous function.

suspend fun SomeService.getString(): String = suspendCoroutine { continuation ->
    getString { continuation.resume(it) }
}

Now you can call this proper suspending version of the function just as in my second code block above.

CodePudding user response:

Honestly, I am not quite sure if I really understand what you try to do but...

is this what you looking for?

    
    private val service = SomeService()
    data class DoWorkResult(val someString: String)

    suspend fun doWork(): DoWorkResult {
        fun onSomeString(someString: String): DoWorkResult {
            //some execution

            val returnResultForDoWork = DoWorkResult(someString)

            //How to return 'returnResultForDoWork' from fun doWork
            return returnResultForDoWork
        }

        return service.getString { someString ->
            onSomeString(someString)
        }
    }

    class SomeService {
        suspend fun getString(onResult: (String) -> DoWorkResult): DoWorkResult {
            delay(1000)
            val myStringFromNetworkOrSomething = "work is done"
            return onResult(myStringFromNetworkOrSomething)
        }
    }
  • Related