Day162 — Convert callback to Coroutine

Jacky Tsang
2 min readJan 10, 2023

Here record the notes I took when I am reading “Avoid Callback Hell With Kotlin Coroutines”.

One-shot callbacks

use suspendCoroutine

suspend fun DatabaseReference.read(): DataSnapshot = suspendCoroutine { continuation ->
val valueEventListener = object : ValueEventListener {
override fun onCancelled(error: DatabaseError) {
continuation.resumeWithException(error.toException())
}

override fun onDataChange(snapshot: DataSnapshot) {
continuation.resume(snapshot)
}
}
addListenerForSingleValueEvent(valueEventListener)
}

suspend fun getGroups(): List<Group> {
return groupRef.read().mapToGroups()
}

class MyViewModel : ViewModel() {
fun getGroups() {
// call it inside a coroutine scope
viewModelScope.launch {
val result = getGroups()
...
}
}
}

wrap callback with suspendCoroutine and make the method, then use resume or resumeWithException at the place where the callback occurs.

Subscription based callbacks

use callbackFlow

suspend fun DatabaseReference.subscribe(): Flow<Player> = callbackFlow {
val childEventListener = object : ChildEventListener {
override fun onChildAdded(snapshot: DataSnapshot, previousChildName: String?) {
trySend(
Player(
snapshot.key
)
)
}
override fun onChildChanged(snapshot: DataSnapshot, previousChildName: String?) {}
override fun onChildRemoved(snapshot: DataSnapshot) {}
override fun onChildMoved(snapshot: DataSnapshot, previousChildName: String?) {}
override fun onCancelled(error: DatabaseError) {}
}
addChildEventListener(childEventListener)
awaitClose {
removeEventListener(childEventListener)
}
}

// subscribe to the data
val flow = DatabaseReference().subscribe()
flow.collect { player ->
...
}

wrap callback listener with callbackFlow, use trySend to send the data, and use awaitClose to close the subscription when needed.

Function vs method

Method is a function associated to an object.

Function is a more general term, and all methods are also functions.

class A {
fun someMethod() {}
}

--

--