代码之家  ›  专栏  ›  技术社区  ›  chroder

如何在序列上异步映射

  •  7
  • chroder  · 技术社区  · 6 年前

    我想遍历一系列对象,并返回异步调用的第一个非空值。

    重点是执行某种可能失败的异步操作,我有一系列的回退,我希望依次尝试(即延迟/不并行)。

    我试过做一些类似于同步呼叫时我会做的事情:

    // 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()
    }
    
    2 回复  |  直到 6 年前
        1
  •  4
  •   Dmytro Rostopira    6 年前

    你得到了一个错误,因为 Sequence 默认情况下是懒惰的 map 不是内联函数,因此未定义其作用域

    你可以避免使用 顺序 通过创建一个懒惰的协程列表

    // 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()
    

    这并没有给出任何错误,而且似乎有效。但我不确定这是惯用的方法

        2
  •  0
  •   jrhy    6 年前

    我发现这个建议 How to asynchronously map over sequence 非常直观。代码位于 https://github.com/Kotlin/kotlin-coroutines-examples/blob/master/examples/suspendingSequence/suspendingSequence.kt 定义 SuspendingIterator 它允许 next() 挂起,然后生成 SuspendingSequence 最重要的是。不幸的是,您需要复制扩展函数,例如 flatMap() , filter() 等,自 悬挂顺序 不能与相关 Sequence 但是我做到了这一点,并且比使用一个频道更高兴看到结果。