【问题标题】:multiple async-await in kotlinkotlin 中的多个异步等待
【发布时间】:2018-12-31 05:50:19
【问题描述】:

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

我不确定,我是否需要对每个查询都设置 async / await,但我只想运行 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
    }
}

【问题讨论】:

    标签: android kotlin async-await coroutine kotlin-coroutines


    【解决方案1】:

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

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

    【讨论】:

    • 这就像您的回答中的简单天堂!你能告诉我更多关于我在哪里可以读到launch(UI) & .also , .takeIf 的信息吗?
    • .also.takeIf 是可用于任何类型的扩展函数,包括 null。它们是标准库的一部分,也是惯用 Kotlin 代码的重要方面。我建议你研究所有这些标准扩展函数并尝试在任何地方使用它们,这样你就会知道它们何时提供价值。
    猜你喜欢
    • 2022-01-18
    • 1970-01-01
    • 2014-05-13
    • 1970-01-01
    • 2019-02-21
    • 2015-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多