协程范围和异步 - 正确的方法?

coroutine scope and async - right approach?

我喜欢协同例程的概念,并且一直在我的 android 项目中使用。目前我正在开发一个 JVM 模块,我将把它包含在一个 Ktor 项目中,我知道 ktor 支持协程。

(找到附加的代码片段)

只是想知道这是正确的方法吗? 我如何使用递归异步?

如果您能推荐任何可以帮助我掌握更深入的协程知识的资源。

提前致谢!

override suspend fun processInstruction(args.. ): List<Any> = coroutineScope {

 val dataWithFields = async{       
     listOfFields.fold(mutableList()){ acc,field ->
    val data = someProcess(field)
    val nested = processInstruction(...nestedField) // nested call

    acc.addAll(data)
    acc.addAll(nested)
    acc
}
}

return@coroutineScope postProcessData(dataWithFields.await())

}

如果你想并行处理所有嵌套调用,你应该将它们中的每一个都包装在 async 中(async 应该在循环内)。然后,在循环之后,您应该 await 所有结果。 (在您的代码中,您 运行 await 紧接在单个 async 之后,因此没有并行执行)。

例如,如果您有 Element:

interface Element {
    val subElements: List<Element>
    suspend fun calculateData(): SomeData
}

interface SomeData

并且您想并行处理所有 subElements 中的 calculateData,您可以这样做:

suspend fun Element.calculateAllData(): List<SomeData> = coroutineScope {
    val data = async { calculateData() }
    val subData = subElements.map { sub -> async { sub.calculateAllData() } }
    return@coroutineScope listOf(data.await()) + subData.awaitAll().flatten()
}

正如你在评论区所说,需要父数据来计算子数据,因此calculateAllData()首先要做的是计算父数据:

suspend fun Element.calculateAllData(
    parentData: SomeData = defaultParentData()
): List<SomeData> = coroutineScope {
    val data = calculateData(parentData)
    val subData = subElements.map { sub -> async { sub.calculateAllData(data) } }
    return@coroutineScope listOf(data) + subData.awaitAll().flatten()
}

现在您可能想知道它的工作速度有多快。考虑以下 Element 实施:

class ElementImpl(override val subElements: List<Element>) : Element {
    override suspend fun calculateData(parentData: SomeData): SomeData {
        delay(1000)
        return SomeData()
    }
}

fun elmOf(vararg elements: Element) = ElementImpl(listOf(*elements))

以及以下测试:

println(measureTime {
    elmOf(
        elmOf(),
        elmOf(
            elmOf(),
            elmOf(
                elmOf(),
                elmOf(),
                elmOf()
            )
        ),
        elmOf(
            elmOf(),
            elmOf()
        ),
        elmOf()
    ).calculateAllData()
})

如果不需要parent-data来计算子数据,它会打印1.06s,因为在这种情况下,所有数据都是并行计算的。否则,它打印 4.15s,因为元素树的高度是 4.