如何在Kotlin中用协程顺序执行多个函数

wwwo4jvm  于 2023-03-03  发布在  Kotlin
关注(0)|答案(1)|浏览(291)

我有六个函数,总的来说,我将分三步执行它们。
首先,变量userPointArray将用于如下所示。

var userPointArray = ArrayList<UserPoint>()

data class UserPoint(
    var userId: String,
    var point: Int,
    var index: Int
)

我的三个步骤将在下面。
1.从firebase获取userId数据并将其放入userPointArray -〉一个函数
1.从firebase获取四个数据,计算point字段-〉四个函数
1.根据点更新index-〉一个函数
由于我需要以三个步骤的顺序执行总共6个函数,因此我将使用Coroutine

步骤1功能

suspend fun userIdToArrayFun() = withContext(Dispatchers.IO) {
        userDB.get()
            .addOnSuccessListener { documents ->
                for (document in documents) {
                    var userId = document.data?.getValue("userId").toString()
                    var userEntry = UserPoint(
                        userId,
                        0,
                        0
                    )

                    userPointArray.add(userEntry)
                    dataToUserPointArray.userIdToArray.value = true
                }
            }
        true
    }

步骤2功能

:此步骤中的四个函数是stepCountToArrayFun()、diaryArrayFun()...

suspend fun calculatePointFun() = withContext(Dispatchers.IO) {
        try {
            listOf(
                launch { stepCountToArrayFun() },
                launch { diaryToArrayFun() },
                launch { commentToArrayFun() },
                launch { likeToArrayFun() }
            ).joinAll()
            false
        } catch (e: Throwable) {
            true
        }
    }

步骤3功能

fun indexArrayFun() = with(Dispatchers.IO) {
        userPointArray.sortWith(compareBy { it.point })

        var initialindex: Int = 1
        for (i in userPointArray.indices) {
            userPointArray[i].point = initialindex

            if (userPointArray[i + 1].point != userPointArray[i].point) {
                initialindex++
            }
        }
        true
    }

最后,我执行这些顺序步骤1 -〉步骤2-〉步骤3如下:

var fullUserArrayPointFun = CoroutineScope(Dispatchers.Default).launch {
        
        if (userIdToArrayFun()) {
            calculatePointFun()
        }
        if(calculatePointFun()) {
            indexArrayFun()
        }
    }

因为我是新的协程,我只是写代码作为搜索,我想我的代码是奇怪的,特别是我的最后一个代码,执行这三个步骤的代码顺序。
你能回顾每三个步骤的代码和我的最后一个代码吗?

z31licg0

z31licg01#

我建议阅读RomanElizarov在Medium上的文档和博客文章,以便在尝试使用它们之前牢牢掌握基本的协程概念。
下面是您发布的代码的简要回顾:

步骤1函数

用suspend函数调用异步函数是没有意义的,你的suspend函数是不会等它的,你可以用suspendCancellableCoroutine把异步函数转换成suspend函数,但是Firebase不需要,因为它已经为您提供了一个suspend函数,您应该使用await() suspend函数,而不是添加异步侦听器。它应该看起来像这样,但是我可能有点偏离,因为我没有使用Firebase,我真的不知道你在for循环中做了什么:

suspend fun userIdToArrayFun() {
    try {
        val documents = userDB.get().await()
        for (document in documents) {
            var userId = document.data?.getValue("userId").toString()
            var userEntry = UserPoint(
                userId,
                0,
                0
            )

            userPointArray.add(userEntry)
            dataToUserPointArray.userIdToArray.value = true
        }
    } catch (e: Exception) {
        // log error? Show error to user?
    }
}

步骤2功能

如果你正在调用的四个函数是 blocking 函数,那么你当前的代码是正确的;如果它们是suspend函数或非blocking函数,那么切换上下文是没有意义的,可以使用coroutineScope {而不是withContext(Dispatchers.IO) {来使代码清晰。
如果这四个函数都是等待结果的suspend函数,那么我对步骤1的评论也适用于您如何实现它们。

步骤3功能

你对with(Dispatchers.IO)的使用使IO成为lambda的接收者--它是with作用域函数,我想你把它和withContext弄混了。
如果这段代码不使用一个巨大的数组,它就不会阻塞,所以你可以删除with Package 器,让它保持原样。但是如果它的数据量足够大,需要花费一定的时间,你可以将它改为一个挂起函数,并使用withContext(Dispatchers.Default)来卸载协同程序上任何线程的工作。我建议使用Default而不是IO,因为它不是IO绑定的。

最后一步

你不应该创建一个CoroutineScope()而不把它存储在一个属性中,这样你就可以管理它的生命周期。如果你真的不需要管理它的生命周期,你可以使用GlobalScope,但是你不关心一个协程的生命周期是非常罕见的。它使用资源,所以当它相关的任何UI过时时,您希望取消协程,这样资源就不会浪费(内存泄漏和/或不必要的CPU/网络/磁盘使用)。
在Android上,绝大多数协程应该从lifecycleScopeviewModelScope启动,因为这些协程作用域已经为您提供,并且在其关联的UI被销毁时会自动取消。
另外,你调用了你的calculatePointFun()两次,这是没有意义的。我会像下面这样写你的代码。你不需要把你的协程的Job赋给一个变量,除非你有一些情况下,你需要提前取消它。
您正在使用userIdToArrayFun(),就好像它返回布尔值一样。您是否希望它在失败时返回false,在成功时返回true?

val fullUserArrayPointJob = viewModelScope.launch {
    if (userIdToArrayFun()) {
        if(calculatePointFun()) {
            indexArrayFun()
        }
    }
}

// or

val fullUserArrayPointJob = viewModelScope.launch {
    if (!userIdToArrayFun()) {
        return@launch
    }
    if(calculatePointFun()) {
        indexArrayFun()
    }
}

相关问题