Skip to content

Instantly share code, notes, and snippets.

@arberg
Created July 15, 2021 20:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save arberg/e8225e63faf04a4c4fc20f6a0aa92649 to your computer and use it in GitHub Desktop.
Save arberg/e8225e63faf04a4c4fc20f6a0aa92649 to your computer and use it in GitHub Desktop.
Kotlin Coroutine Callback for result/error/cancel
import dk.bnr.androidbooking.managers.model.AppInternalSilentException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
interface OnResultCallback<T> {
fun onNext(value: T)
fun onError(throwable: Exception)
fun onCancel()
/**
* Waits for result, and returns it
*/
suspend fun await(): T
}
class DefaultOnResultCallback<T> : OnResultCallback<T> {
// An alternative to this is using the callbackFlow. Though note that the callbackFlow can also deliver more values, but so can Channels. The callbackFlow is also implemented using channels.
// See also callbackFlow: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/callback-flow.html
// See https://developer.android.com/kotlin/flow#callback
sealed class Result<T> {
class Success<T>(val value: T) : Result<T>()
class Failure<T>(val throwable: Exception) : Result<T>()
class Cancelled<T> : Result<T>()
}
private val channel = Channel<Result<T>>(1)
// See also callbackFlow: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/callback-flow.html
// does not work with MutableSharedFlow, see callbackFlow above
override fun onNext(value: T) {
send(Result.Success(value))
}
override fun onError(throwable: Exception) {
send(Result.Failure(throwable))
}
override fun onCancel() {
send(Result.Cancelled())
}
private fun send(result: Result<T>) {
CoroutineScope(Dispatchers.Main).launch {
channel.send(result)
}
}
override suspend fun await(): T {
val result = channel.receive()
return when (result) {
is Result.Success -> result.value
is Result.Failure -> throw result.throwable
is Result.Cancelled -> throw AppInternalSilentException("Cancelled")
}
}
}
@arberg
Copy link
Author

arberg commented Jul 15, 2021

Can be used with

CoroutineScope(Dispatchers.Main).launch { 
    DefaultOnResultCallback<Profile>().also {
                    somethingThatGeneratesAProfileOrAnErrorOrCancels(it)
                }.await() 
                // also Catch and ignore cancel exception somewhere here or en exceptionHandler
}
                ```

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment