我要遍历一系列对象并返回异步调用的第一个非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()
}
答案 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()