使用Arrow-kt和Kotlin处理异步结果
我有两个对外部系统的异步函数调用,返回任何一个<Exception,Something>,并且需要合并它们的结果。作为 Arrow-Kt 函数式编程的初学者,我想知道完成这项任务的最佳方式是什么。以下是我目前正在使用的代码。它当然有效,但并不真正“感觉”是最直接的。我正在寻找一种更“实用”的风格来获得结果。注意:需要预先使用成功的 List 结果。
suspend fun getAs(): Either<Exception, List<A>> = TODO()
suspend fun getBs(): Either<Exception, List<B>> = TODO()
suspend fun doSomethingWithA(listA: List<A>): Unit = TODO()
launch {
val deferredA = async { getAs() }
val deferredB = async { getBs() }
either<Exception, List<A>> {
val listOfAs = deferredA.await()
.bimap(leftOperation = { e ->
println("special message on error for A")
e
}, rightOperation = { listA ->
doSomethingWithA(listA)
listA
})
.bind()
val listOfBs = deferredB.await().bind()
listOfAs.filter { it.someId !in listOfBs.map { it.someProperty } }
}
.map { /* handle result */ }
.handleError { /* handle error */ }
}
另一种选择是map{}像这样使用函数
launch {
val deferredA = async { getAs() }
val deferredB = async { getBs() }
deferredA.await()
.bimap(leftOperation = { e ->
println("special message on error for A")
e
}, rightOperation = { listA ->
doSomethingWithA(listA)
deferredB.await().map { listB ->
listA.filter { a -> a.someId !in listB.map { it.someProperty } }
}
})
.map { /* handle result */ }
.handleError { /* handle error */ }
}
回答
最简单的方法是结合either { }使用parZip。
either { }允许你提取A的Either<E, A>,并且parZip是用于运行效用函数suspend并行功能。
suspend fun getAs(): Either<Exception, List<A>> = TODO()
suspend fun getBs(): Either<Exception, List<B>> = TODO()
suspend fun doSomethingWithA(listA: List<A>): Unit = TODO()
either {
val list = parZip(
{
getAs()
.mapLeft { e -> println("special message on error for A"); e }
.bind()
},
{ getBs().bind() },
{ aas, bbs ->
aas.filter { a -> a.someId !in bbs.map { it.someProperty }
}
)
/* Work with list and return value to `either { } */
}.handleError { /* handle error */ }
这里bind()摘录A自Either<E, A>. 我们在内部parZip这样做,这样每当Left遇到a时,它就会使either { }块短路,通过这样做,它还取消了仍在运行的任务parZip。
这样,如果getAs()立即返回Left,然后它成为产值either { }和getBs()被取消。