Reputation: 1341
I currently have a callback that instead of returning a value synchronously, it will need to do some async work and then return the result. I've been investigating about how to do it but I don't get thing clear. What I have achieved until now is this, but as I need to put a return into foo method, at first it returns "something" and afterthat return the result of calling someMethod().
Is it possible to to what I'm pretending?
class CustomClass() :
SomeClass.SomeInterface {
override fun foo(p0: SomeType): String {
val result = "something"
MainScope().launch {
withContext(Dispatchers.IO) {
result = someMethod()
}
}
return result
}
suspend fun someMethod(): String =
suspendCancellableCoroutine { cancelableContinuation ->
//TODO: register some listener that will end up calling
cancelableContinuation.resume("some_value", {})
}
Thanks in advance!
Upvotes: 5
Views: 7560
Reputation: 93609
You cannot return the result synchronously, so foo
has to be defined as a suspend function.
Also, there's no reason to wrap a suspend function call in withContext
. Suspend functions (if composed correctly) internally wrap any blocking code so they are safe to call from any dispatcher, so there's no reason to specify one using withContext
If you strip away the withContext
and the launching of the coroutine that you weren't waiting for, foo()
does nothing besides calling someMethod
, so you can just move that code from someMethod
into foo
. But foo
must be defined as a suspend
function.
override suspend fun foo(p0: SomeType): String = suspendCancellableCoroutine { continuation ->
val request = someApi.request(p0) { result: String ->
continuation.resume(result)
}
continuation.invokeOnCancellation {
request.cancel()
}
}
Generally you don't want model classes like this launching coroutines from their own CoroutineScopes. Just have them expose suspend functions. It's up to the higher level classes like Activity, Fragment, and ViewModel to launch coroutines from their desired scopes so they can control the coroutine lifecycles.
Upvotes: 4
Reputation: 7612
Your foo
function is basically synchronous, so what's happening is that you launch a suspendable
don't wait for it's result and return the initial value of result ("something"
)
You have multiple options for making sure, foo waits for the response from someMethod
, for ex:
foo
a `suspend funoverride fun foo(p0: SomeType, onResult: (String) -> Unit): String {
MainScope().launch {
withContext(Dispatchers.IO) {
onResult(someMethod())
}
}
}
// Call it like this:
foo(p0) { result ->
// handle result
}
Upvotes: 1