android - kotlin中的多个异步等待

标签 android kotlin async-await coroutine kotlin-coroutines

promoType 中的对象 = [字符串列表] 它更像是 10 个 firebase 查询在这里运行,查找 10 个特定的节点集并进一步向下。

我不确定我是否需要对每个查询进行异步/等待,但我只想运行这些查询中的 10 个,然后判断 couponKey 是否为空。我只想显示输入的优惠券是否正确。

进一步,在changeUserType(couponKey, couponFoundAtKey)中,发生了一些数据库写操作。

fun checkPromo(promoCodeET: String) = async(UI) {
    try {
        val database = PersistentFirebaseUtil.getDatabase().reference
        val job = async(CommonPool) {

            for (obj in promoType) {
                val query = database.child("promos").child(obj).orderByChild("promoCode").equalTo(promoCodeET)

                query.addListenerForSingleValueEvent(object :
                        ValueEventListener {
                    override fun onDataChange(dataSnapshot: DataSnapshot) {
                        if (dataSnapshot.exists()) {
                            couponKey = dataSnapshot.key.toString()
                            couponFoundAtKey = dataSnapshot.children.first().key.toString()
                            if (couponKey.isNotEmpty())
                                changeUserType(couponKey, couponFoundAtKey)
                            flag = true
                        }
                    }

                    override fun onCancelled(error: DatabaseError) {
                        // Failed to read value
                    }
                })
                if (flag) break
            }
        }
        job.await()            

    }
    catch (e: Exception) {
    }
    finally {
        if (couponKey.isEmpty()){
            Toast.makeText(this@Coupon, "Invalid coupon", Toast.LENGTH_LONG).show()
        }
        flag = true
    }
}

最佳答案

我发现您的代码有几处错误:

  1. 你有一个没有意义的外部 async(UI)
  2. 你的内部 async(CommonPool) 也没有意义,因为你的数据库调用已经是异步的
  3. 您在 async 之后立即使用 await 的反模式,使其不是真正的“异步”(但见上文,不管有没有这个,整个事情都是异步的)
  4. 您的抓取功能有改变用户类型的副作用
  5. 要将结果传输给调用者,您再次使用副作用而不是返回值

您的代码应该更简单。您应该声明一个 suspend fun ,其返回值为 (couponKey, coupon) 对:

suspend fun fetchPromo(promoType: String, promoCodeET: String): Pair<String, String>? =
    suspendCancellableCoroutine { cont ->
        val database = PersistentFirebaseUtil.getDatabase().reference
        val query = database.child("promos").child(promoType)
                .orderByChild("promoCode").equalTo(promoCodeET)
        query.addListenerForSingleValueEvent(object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {
                cont.resume(
                    dataSnapshot
                        .takeIf { it.exists() }
                        ?.let { snapshot ->
                            snapshot.key.toString()
                                .takeIf { it.isNotEmpty() }
                                ?.let { key ->
                                    Pair(key, snapshot.children.first().key.toString())
                                }
                        }
                )
            }

            override fun onCancelled(error: DatabaseError?) {
                if (error != null) {
                    cont.resumeWithException(MyException(error))
                } else {
                    cont.cancel()
                }
            }
        })
    }

要调用此函数,请在调用站点使用 launch(UI)。获得非空值后更改用户类型:

launch(UI) {
    var found = false
    for (type in promoType) {
        val (couponKey, coupon) = fetchPromo(type, "promo-code-et") ?: continue
        found = true
        withContext(CommonPool) {
            changeUserType(couponKey, coupon)
        }
        break
    }
    if (!found) {
        Toast.makeText(this@Coupon, "Invalid coupon", Toast.LENGTH_LONG).show()
    }
}

你说 changeUserType 执行一些数据库操作,所以我将它们包装在 withContext(CommonPool) 中。

另请注意,我在函数外部提取了促销类型循环。这将导致查询顺序执行,但您可以编写不同的调用代码来实现并行查找:

var numDone = 0
var found = false
promoType.forEach { type ->
    launch(UI) {
        fetchPromo(type, "promo-code-et")
            .also { numDone++ }
            ?.also { (couponKey, coupon) ->
                found = true
                launch(CommonPool) {
                    changeUserType(couponKey, coupon)
                }
            }
            ?: if (numDone == promoType.size && !found) {
                Toast.makeText(this@Coupon, "Invalid coupon", Toast.LENGTH_LONG).show()
            }
    }
}

关于android - kotlin中的多个异步等待,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51484512/

相关文章:

java - getFilesDir() 导致空指针异常

android - Google Firestore 保存用户输入 Kotlin

kotlin - 如何在 Kotlin Native 中使用 Http Request lib

java - 为什么 imageView 没有居中?

android - Tab 过渡图标,如 Tinder

android - 如何在 webview 中加载 base64 字符串?

java - 尝试显示/取消隐藏 LinearLayout 时无法调用 setVisibility 方法

c# - 异步方法调用和模拟

node.js - 保存方法在 Mongoose 中不起作用

javascript - 如何在 JS 异步回调函数中返回一个值 - gapi