链式 kotlin 流程取决于结果状态

Chain kotlin flows depends on Result state

我正在寻找最“干净”的方式来实现以下逻辑:

最明显的方法是:

methodA().map { methodAResult ->
  when (methodAResult) {
    is Result.Success -> {
      methodB(methodAResult).map { methodBResult ->
        when (methodBResult) {
          is Result.Success -> {
            methodC(methodAResult).map { methodCResult ->
              when (methodCResult) {
                is Result.Success -> TODO()
                is Result.Failure -> TODO()
              }
            }
          }
          is Result.Failure -> TODO()
        }
      }
     }
     is Result.Failure -> TODO()
   }
 }

但它看起来像一个众所周知的“回调地狱”。你有什么办法可以避免吗?

我相信在这个用例中,您应该使用 suspend 函数并 compose 使用 await() 函数。 错误应按照 here.

中所述通过异常传递

我相信这可以用 transform operator:

methodA().transform { methodAResult ->
    when (methodAResult) {
        is Success -> methodB(methodAResult).collect { emit(it) }
        is Failure -> TODO()
    }
}.transform { methodBResult ->
    when (methodBResult) {
        is Success -> methodC(methodBResult).collect { emit(it) }
        is Failure -> TODO()
    }
}.transform { methodCResult ->
    when (methodCResult) {
        is Success -> TODO()
        is Failure -> TODO()
    }
}

对Михаил Нафталь提供的解决方案稍作修改

    methodA()
        .flatMapMerge {
            when (it) {
                is Result.Success -> methodB(it)
                is Result.Failure -> emptyFlow()
            }
        }.flatMapMerge {
            when (it) {
                is Result.Success -> methodC(it)
                is Result.Failure -> emptyFlow()
            }
        }.collect {
            when (it) {
                is Result.Success -> TODO()
                is Result.Failure -> TODO()
            }
        }

将一个流的输出合并到另一个流是 flatMap 的目标,因此使用 flatMap 似乎更简洁一些。

如果此结果 class 具有 mapfoldgetOrNull 类型的方法,则可以进一步清理并删除 when 块.

此外,如果您需要传播要收集的故障,则可以将对 emptyFlow 的调用替换为仅输出所需故障的流。

很遗憾 flatMap 方法仍然不存在。

但是你可以使用 mapCatching :

methodA
    .mapCatching { a -> methodB(a).getOrThrow() }
    .mapCatching { b -> methodC(b).getOrThrow() }

或者制作您自己的 flatMap 扩展函数:

fun <T, R> Result<T>.flatMap(block: (T) -> (Result<R>)): Result<R> {
    return this.mapCatching {
        block(it).getOrThrow()
    }
}

methodA
    .flatMap { a -> methodB(a) }
    .flatMap { b -> methodC(b) }