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:
- call
service.getString
- call
onSomeString(someString)
when a someString is return fromservice.getString
- in
onSomeString
analyse/handle asomeString
and return (how?) aDoWorkResult(someString)
fromdoWork
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)
}
}