wrapping callbacks that fire more than once
See original GitHub issueI am now trying to convert some Firebase code on Android to use coroutines. some Firebase callbacks are a bit weird in that they sometimes trigger more than once. For example for getting the download url from firebase storage I have written this:
private suspend fun getFileDownloadUrlAsync(file_sref: StorageReference): Uri = suspendCoroutine { c ->
with(file_sref.downloadUrl) { //<---- call to Firebase getDownloadUrl()
addOnCompleteListener { //<--- this callback can trigger more than once!
if (it.isSuccessful) {
c.resume(it.result)
} else
c.resumeWithException(Exception("some error"))
}
addOnFailureListener {
c.resumeWithException(it)
}
}
}
Since the completion listener is triggered more than once I get an IllegalStateException(“Already resumed”) As a work-around I have defined
class WrappedContinuation<T>(val c: Continuation<T>) : Continuation<T> {
var isResolved = false
override val context: CoroutineContext
get() = c.context
override fun resume(value: T) {
if (!isResolved) {
isResolved = true
c.resume(value)
}
}
override fun resumeWithException(exception: Throwable) {
if (!isResolved) {
isResolved = true
c.resumeWithException(exception)
}
}
}
public inline suspend fun <T> suspendCoroutineW(crossinline block: (WrappedContinuation<T>) -> Unit): T =
suspendCoroutine { c ->
val wd = WrappedContinuation(c)
block(wd)
}
and I am using suspendCoroutineW
instead of suspendCoroutine
. Would it be possible to
modify SafeContinuation
class in the library to offer similar functionality, since it already keeps the information about if the continuation has already been resolved or not, and expose it to the user?
Issue Analytics
- State:
- Created 6 years ago
- Reactions:3
- Comments:8 (3 by maintainers)
Top Results From Across the Web
Preventing callbacks from accidentally being called twice
Preventing callbacks from accidentally being called twice · Use an else branch even if it's not necessary · Wrap the callback so calling...
Read more >Callback Gotchas | Dash for Python Documentation | Plotly
In this situation, where a component registered with a callback is missing from the layout, the callback will fail to fire. For example,...
Read more >Exploring RxJava - Wrapping a Listener Callback - Couchbase
A brief tutorial showing how to take a callback-based listener style API (think button clicks) to a reactive one using Observables with ...
Read more >Existing 3-function callback to Kotlin Coroutines - Stack Overflow
Chaining callbacks works if there ale multiple callbacks in sequence, each providing a result or error. <s>Here are two callbacks in parallel, ...
Read more >Common mistakes with React Testing Library - Kent C. Dodds
Because of this, the callback can be called (or checked for errors) a non-deterministic number of times and frequency (it's called both on...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
ok so I find the solution: I should have used suspendCancellableCoroutine
suspendCancellableCoroutine
build aSuspendableCoroutine
that exposes (among other stuff) the two methodsthat basically do what I want: they check the continuation state (that I cannot access directly because it is internal data), if it was already resumed. About Firebase: for that specific API (getFileDownloadUrl) the callback is called twice almost always. There are also other Firebase APIs that behave the same way. I don’t know the cause. It is a documented behavior, but I never actually understood why this should happen at all. But this is how it works.