如何使用协程异步处理数据?

huangapple go评论111阅读模式
英文:

How to process data asynchronously using coroutines?

问题

我需要执行4个并行请求。以下是我的代码:

suspend fun fetchAsyncData() = coroutineScope {
    val first = async { repository.taskFirst() }
    val second = async { repository.taskSecond() }
    val third = async { repository.taskThird() }
    val fourth = async { repository.taskFourth() }

    val firstResult = first.await()
    val secondResult = second.await()
    val thirdResult = third.await()
    val fourthResult = fourth.await()
    
}

问题是,使用这种方法,请求是并行执行的,但我可能同时获取答案。也就是说,如果某些请求将执行45秒,而其他请求将执行3秒,那么我只能在45秒后处理我的请求结果。
我的任务是,一旦收到第一个请求的答案,立即将其传递给视图模型,以便它可以在片段中显示这一部分数据。随后,一旦收到另一个请求的响应,再传递更多的数据,依此类推。

请问如何实现这一点,帮助我一下?

英文:

I need to execute 4 parallel requests. Here is my code:

suspend fun fetchAsyncData() = coroutineScope {
    val first = async { repository.taskFirst() }
    val second = async { repository.taskSecond() }
    val third = async { repository.taskThird() }
    val fourth = async { repository.taskFourth() }

    val firstResult = first.await()
    val secondResult = second.await()
    val thirdResult = third.await()
    val fourthResult = fourth.await()
    
}

The problem is that with this approach, requests are executed in parallel, but I can get answers at the same time. That is, if some of the requests will be executed for 45 seconds, and some for 3 seconds, then I will be able to process the results of my requests only after 45 seconds.
My task is that as soon as the answer to the first request is received, pass it to the view model, so that it, in turn, can display this piece of data in a fragment. Further, as soon as another response to the request is received, transfer one more data, and so on.

How can this be done, please help me?

答案1

得分: 1

也许类似这样的代码对你有用

```kotlin
fun getRepoFlow() = channelFlow {

        coroutineScope {
            launch { send(async { "1" }.await()) }
            launch { send(async { delay(1000L); "2" }.await()) }
            launch { send(async { "3" }.await()) }
        }

    }

<details>
<summary>英文:</summary>

Maybe something like this could work for you:

fun getRepoFlow() = channelFlow {

    coroutineScope {
        launch { send(async { &quot;1&quot; }.await()) }
        launch { send(async { delay(1000L); &quot;2&quot; }.await()) }
        launch { send(async { &quot;3&quot; }.await()) }
    }

}

</details>



# 答案2
**得分**: 1

如果所有任务返回相同的结果,那么您可以创建一个流并将数据异步发送到其中。以下是一个示例:

```kotlin
fun main() {
    // 在您的 viewModel 中创建一个流对象
    val flow: MutableStateFlow&lt;Data?&gt; = MutableStateFlow(null)

    // 观察流。例如,在 Activity 的生命周期中(适用于 Android)
    CoroutineScope(Dispatchers.Unconfined).launch {
        flow.collect {
            println(it)
        }
    }

    // 从 viewModel 获取数据
    CoroutineScope(Dispatchers.Unconfined).launch {
        launch {
            flow.value = task1()
        }

        launch {
            flow.value = task2()
        }
    }

    // 应用程序的生命周期。在实际应用中不需要这样做。
    while (true) {}
}

suspend fun task1(): Data {
    delay(2000)
    return Data(1, Date())
}

suspend fun task2(): Data {
    delay(1000)
    return Data(2, Date())
}

data class Data(val d: Int, val time: Date)

但如果 tasks 返回不同的数据,那么您可以创建多个 flow,并在每个任务返回时发送数据。

英文:

If all the tasks return the same result then you can create a flow and send data to it asynchronously. Here's an example:

fun main() {
    // a flow object inside your viewModel
    val flow: MutableStateFlow&lt;Data?&gt; = MutableStateFlow(null)

    // observe the flow. e.g. For android in Activity&#39;s lifecycle
    CoroutineScope(Dispatchers.Unconfined).launch {
        flow.collect {
            println(it)
        }
    }

    // get data from viewModel
    CoroutineScope(Dispatchers.Unconfined).launch {
        launch {
            flow.value = task1()
        }

        launch {
            flow.value = task2()
        }
    }

    // app&#39;s lifespan. Not needed in practical applications.
    while (true) {}
}

suspend fun task1(): Data {
    delay(2000)
    return Data(1, Date())
}

suspend fun task2(): Data {
    delay(1000)
    return Data(2, Date())
}

data class Data(val d: Int, val time: Date)

But if the tasks return different data, then you can create multiple flow and send data when each task returns.

答案3

得分: 1

使用这种方法,您可以处理不同类型的响应和失败情况。

sealed class ResponseState {
    data class Content<T>(val data: T) : ResponseState()
    // 处理网络和服务器错误状态
    // data class Error(e: Throwable): ResponseState()
}

val flow: MutableStateFlow<ResponseState?> = MutableStateFlow(null)
coroutineScope {
    launch {
        val res = repository.taskFirst() // 返回类型 a
        flow.value = ResponseState.Content<a>(res)
    }
    launch {
        val res = repository.taskSecond() // 返回类型 b
        flow.value = ResponseState.Content<b>(res)
    }
    launch {
        val res = repository.taskThird() // 返回类型 c
        flow.value = ResponseState.Content<c>(res)
    }
    launch {
        val res = repository.taskFourth() // 返回类型 d
        flow.value = ResponseState.Content<d>(res)
    }
}

// 这里是一个使用 Flow 的示例,您也可以使用 LiveData
CoroutineScope(Dispatchers.Main).launch {
    flow.collect {
        when (it) {
            is ResponseState.Content<*> -> {
                // 检查响应类型
                when (it.data) {
                    is a -> {
                        // 处理 taskFirst 响应
                    }
                    is b -> {
                        // 处理 taskSecond 响应
                    }
                    is c -> {
                        // 处理 taskThird 响应
                    }
                    is d -> {
                        // 处理 taskFourth 响应
                    }
                    else -> {}
                }
            }
        }
    }
}

注意:我注意到您的代码中存在一些 HTML 转义字符(例如<和>),这些字符在代码中通常不需要。我已经将其保留在翻译中,但您在实际代码中应该删除它们。

英文:

With this approach you can handle different type of response and failure case.

       sealed class ResponseState {
            data class Content&lt;T&gt;(val data: T) : ResponseState()
            // handle network and server error state
            //data class error(e : Throwable): ResponseState()

        }

        val flow: MutableStateFlow&lt;ResponseState?&gt; = MutableStateFlow(null)
        coroutineScope {
            launch {
                val res = repository.taskFirst() // return type a
                flow.value = ResponseState.Content&lt;a&gt;(res)
            }
            launch {
                val res = repository.taskSecond()// return type b
                flow.value = ResponseState.Content&lt;b&gt;(res)
            }
            launch {
                val res = repository.taskThird() // return type c
                flow.value = ResponseState.Content&lt;c&gt;(res)
            }
            launch {
                val res = repository.taskFourth() // return type d
                flow.value = ResponseState.Content&lt;d&gt;(res)
            }
        }
        // here is an example with flow, you can use live data as well
        CoroutineScope(Dispatchers.Main).launch {
            flow.collect {
                when (it) {
                    is ResponseState.Content&lt;*&gt; -&gt; {
                        // check response type
                        when (it.data) {
                            is a -&gt; {
                                //handle taskFirst response
                            }

                            is b -&gt; {
                                //handle taskSecond response
                            }

                            is c -&gt; {
                                //handle taskThird response
                            }

                            is d -&gt; {
                                //handle taskFourth response
                            }

                            else -&gt; {}
                        }
                    }
                }
            }
        }

答案4

得分: 0

你应该在async之后使用每一行的代码。示例:

suspend fun fetchAsyncData() = coroutineScope { 

    val first = async { repository.taskFirst() }
    val firstResult = first.await()
    // 在这里使用 firstResult

    val second = async { repository.taskSecond() }
    val secondResult = second.await()
    // 在这里使用 secondResult

    val third = async { repository.taskThird() }
    val thirdResult = third.await()
    // 在这里使用 thirdResult

    val fourth = async { repository.taskFourth() }
    val fourthResult = fourth.await()
    // 在这里使用 fourthResult

}

并调用`fetchAsyncData()`函数

```kotlin
lifecycleScope.launch {
    fetchAsyncData()
}
英文:

you should use every single line code after async. example:

suspend fun fetchAsyncData() = coroutineScope { 

val first = async { repository.taskFirst() }
val firstResult = first.await()
//use firstResult here

val second = async { repository.taskSecond() }
val secondResult = second.await()
//use secondResult here


val third = async { repository.taskThird() }
val thirdResult = third.await()
//use thirdResult here

val fourth = async { repository.taskFourth() }
val fourthResult = fourth.await()
//use fourthResult here

}

and call fetchAsyncData() function:

lifecycleScope.launch {
        fetchAsyncData()
    }

huangapple
  • 本文由 发表于 2023年7月11日 03:34:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76656791.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定