如何异步映射序列

时间:2018-08-22 14:42:41

标签: kotlin kotlinx.coroutines

我要遍历一系列对象并返回异步调用的第一个非null。

关键是要执行某种可能会失败的异步操作,并且我要依次尝试进行一系列后备操作(即懒惰/不并行)。

我试图做一些与同步调用类似的事情:

// ccs: List<CurrencyConverter>
override suspend fun getExchangeRateAsync(from: String, to: String) =
  ccs.asSequence()
    .map { it.getExchangeRateAsync(from, to) }
    .firstOrNull { it != null }
    ?: throw CurrencyConverterException()

IntelliJ抱怨:

  

只能在协程体内调用悬浮函数

编辑:为澄清起见,如果在列表上进行映射,这将按预期工作,但是我想看看如何对序列进行此操作。

所以我想这是因为地图lambda没有被暂停吗?但是我不知道该怎么做。我尝试了许多不同的方法,但似乎都没有效果。我找不到任何例子。

如果我使用带有异步块的for循环以更具过程性的方式重新编写此代码,则可以使其正常工作:

override suspend fun getExchangeRateAsync(from: String, to: String) {
    for (cc in ccs) {
        var res: BigDecimal? = async {
            cc.getExchangeRateAsync(from, to)
        }.await()

        if (res != null) {
            return res
        }
    }

    throw CurrencyConverterException()
}

3 个答案:

答案 0 :(得分:4)

您遇到了错误,因为默认情况下Sequence是惰性的,并且map不是内联函数,因此没有定义范围

您可以通过创建懒惰的协程列表来避免使用Sequence

// ccs: List<CurrencyConverter>
suspend fun getExchangeRateAsync(from: String, to: String) =
    ccs
    .map { async(start = CoroutineStart.LAZY) { it.getExchangeRateAsync(from, to) } }
    .firstOrNull { it.await() != null }
    ?.getCompleted() ?: throw Exception()

这没有给出任何错误,并且似乎可以正常工作。但是我不确定这是惯用的方式

答案 1 :(得分:0)

FWIW,我发现How to asynchronously map over sequence中的建议非常直观。 https://github.com/Kotlin/kotlin-coroutines-examples/blob/master/examples/suspendingSequence/suspendingSequence.kt处的代码定义了SuspendingIterator,该代码允许next()挂起,然后在其之上构建SuspendingSequence。不幸的是,您需要复制扩展功能,例如flatMap()filter()等,因为SuspendingSequence不能与Sequence相关,但是我做到了这一点,并且对它更满意结果要比使用渠道好。

答案 2 :(得分:0)

我建议用Flow替换Sequence。 Flow api和行为与Sequence几乎相同,但具有挂起选项。

https://kotlinlang.org/docs/reference/coroutines/flow.html

代码:

override suspend fun getExchangeRateAsync(from: String, to: String) =
    ccs.asFlow()
    .map { it.getExchangeRateAsync(from, to) }
    .firstOrNull { it != null }
    ?: throw CurrencyConverterException()