Rosário P. Fernandes
Rosário P. Fernandes

Reputation: 11344

How do I call a suspend function from a callback?

I have a suspendable (updateData) function which takes another suspend function as an argument (transform). In my updateData function I'm making a call to an asynchronous API and I need to pass the result to the transform suspend function. My current problem is that calling the transform function shows the message "suspension functions can be called only within coroutine context".

Here's what the code looks like:

override suspend fun updateData(transform: suspend (prefs: Preferences) -> Preferences): Preferences {
    return suspendCancellableCoroutine { continuation ->
        realtimeDatabase.runTransaction(object : Transaction.Handler {
            override fun doTransaction(currentData: MutableData): Transaction.Result {
                val prefs: Preferences = currentData.toPreferences()

                // I need to call the transform() function here
                
                // transform(prefs)
                // This call shows the error "suspension functions can be called only within coroutine context"
                
                return Transaction.success(currentData)
            }

            override fun onComplete(
                error: DatabaseError?,
                committed: Boolean,
                currentData: DataSnapshot?
            ) {
                if (error != null) {
                    continuation.resumeWithException(error)
                } else {
                    continuation.resume(currentData.toPreferences())
                }
            }
        })
    }
}

I found this similar question, but it doesn't really solve my problem because I can't call the transform function outside of doTransaction (I need currentData).

Also, I can't make transform a normal "non-suspend" function because I'm overriding that function from another class.

My question is: How can I apply the transform suspend function to currentData?

Upvotes: 2

Views: 1436

Answers (1)

Tenfour04
Tenfour04

Reputation: 93834

I don't know exactly what your API is here, but maybe you can break this function up to do your transformation after the suspendCoroutine block so its being called inside the coroutine instead of in the API callback.

override suspend fun updateData(transform: suspend (prefs: Preferences) -> Preferences): Preferences {
    val retrievedPrefs = suspendCancellableCoroutine { continuation ->
        realtimeDatabase.runTransaction(object : Transaction.Handler {
            override fun doTransaction(currentData: MutableData): Transaction.Result {
                return Transaction.success(currentData)
            }

            override fun onComplete(
                error: DatabaseError?,
                committed: Boolean,
                currentData: DataSnapshot?
            ) {
                if (error != null) {
                    continuation.resumeWithException(error)
                } else {
                    continuation.resume(currentData.toPreferences())
                }
            }
        })
    }
    return transform(retrievedPrefs)
}

Upvotes: -1

Related Questions