You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Kotlin协程执行Firebase Transaction操作?

解决方案

第一步:自定义runTransaction协程扩展

kotlinx-coroutines-play-services库仅为Firebase返回Task的API提供了await()扩展,而runTransaction走回调接口逻辑,没有返回Task,需要自行封装协程适配:

import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.MutableData
import com.google.firebase.database.Transaction
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException

suspend fun DatabaseReference.awaitRunTransaction(
    fireLocalEvents: Boolean = false,
    doTransaction: (MutableData) -> Transaction.Result
): DataSnapshot? = suspendCancellableCoroutine { cont ->
    runTransaction(object : Transaction.Handler {
        override fun doTransaction(mutableData: MutableData): Transaction.Result {
            return doTransaction(mutableData)
        }

        override fun onComplete(error: DatabaseError?, committed: Boolean, snapshot: DataSnapshot?) {
            when {
                error != null -> cont.resumeWithException(error.toException())
                else -> cont.resume(snapshot)
            }
        }
    }, fireLocalEvents)

    // 协程取消时终止事务的可选处理逻辑
    cont.invokeOnCancellation {
        this@awaitRunTransaction.onDisconnect()
    }
}

第二步:改造业务代码

原有代码在suspend函数内手动创建CoroutineScope启动协程,会脱离当前协程作用域,导致取消、异常无法向上传递,直接删除多余的launch调用即可,改造后完整代码如下:

override suspend fun modifyProductStock(
    product: ProductModel,
    valueToModify: Long,
    replace: Boolean
) {
    val restaurantId = authRepository.restaurantId.value ?: throw Exception("No restaurant!")
    val productId = product.id ?: throw Exception("No Product ID!")
    val reference = FirebaseDatabase.getInstance().getReference("database/$restaurantId").child("products")
    val stockRef = reference.child(productId).child("stock")

    if (replace) {
        stockRef.setValue(valueToModify).await()
    } else {
        stockRef.awaitRunTransaction { mutableData ->
            // 此处写入你的事务操作逻辑,示例为叠加库存:
            val currentStock = mutableData.getValue(Long::class.java) ?: 0L
            mutableData.value = currentStock + valueToModify
            Transaction.success(mutableData)
        }
    }
}

说明

  • 扩展函数的fireLocalEvents参数对应原生runTransaction的同名参数,默认设为false可避免本地临时变更触发不必要的监听回调,可根据业务需求调整
  • 事务执行过程中如果出现冲突、网络错误等异常,会直接抛出到协程中,你可以在调用modifyProductStock的位置用try-catch捕获处理异常

内容的提问来源于stack exchange,提问作者George O

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 07:27:04