【问题标题】:How do I call a suspend function from a callback?如何从回调中调用挂起函数?
【发布时间】:2021-12-21 10:26:31
【问题描述】:

我有一个可挂起的 (updateData) 函数,它接受另一个挂起函数作为参数 (transform)。在我的updateData 函数中,我正在调用异步API,我需要将结果传递给transform 挂起函数。 我目前的问题是调用transform 函数显示消息“只能在协程上下文中调用暂停函数”。

代码如下:

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())
                }
            }
        })
    }
}

我找到了this similar question,但它并没有真正解决我的问题,因为我无法在doTransaction 之外调用transform 函数(我需要currentData)。

另外,我无法将 transform 设为正常的“非挂起”函数,因为我正在从另一个类覆盖该函数。

我的问题是:如何将transform 挂起功能应用于currentData

【问题讨论】:

  • 非挂起函数不能在任何其他等待中等待,只能通过阻塞来等待。因此,如果您无论如何都无法重新设计代码以在回调之外执行此转换(因此在它之前/之后),那么我认为唯一可能的方法是使用runBlocking()
  • @broot 谢谢,我会考虑使用runBlocking()

标签: kotlin kotlin-coroutines


【解决方案1】:

我不知道你的 API 到底是什么,但也许你可以在 suspendCoroutine 块之后分解这个函数来进行转换,以便在协程而不是 API 回调中调用它。

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)
}

【讨论】:

  • 感谢您的回答,但不幸的是我不能这样做,因为doTransaction 方法应该在返回事务成功之前将转换后的值写入数据库。
  • 这是什么数据库库?至少有一种方法可以避免在主线程上调用这个doTransaction 函数,否则runBlocking 会出现问题。
猜你喜欢
  • 2020-04-13
  • 2022-06-24
  • 2021-04-09
  • 2021-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-30
  • 1970-01-01
相关资源
最近更新 更多