将未来放在 OnFailure 中
Put future inside OnFailure
我是 scala 的新手,所以要温柔!
我正在尝试创建一个 "logical transaction",这是一个代码示例:
val f1:Future[Int] = dao.insertIntoDB
f1.flatmap{
x => {
val f2 = sendHttpRequestFuture
f.onFailure{
case t => dao.revertDbChangeFuture
}
}
f1.onFailure{
logger.error("error)
}
所以我认为会发生的是先执行外层的onFailure,然后是内层的onFailure。
问题是 onFailure return 单元,所以把 future 放在 onFailure 里面感觉很奇怪,我不确定处理这种用例的正确方法是什么。
我基本上需要还原我的更改,还原操作是异步的。
你能给点建议吗?
谢谢!
你不应该使用 revertDbChangeFuture
,因为一旦你有了那个未来,它通常意味着你已经触发了底层计算,即你已经在恢复数据库更改,无论结果如何的插入。
相反,您应该添加 方法 以在 onFailure
回调中恢复数据库更改:
insertIntoDBFuture.onFailure {
case t => revertDbChange()
}
val f1 = insertIntoDBFuture.flatmap(sendHttpRequestFuture(_))
f1.onFailure{
case t => logger.error("error", t)
}
f1
如果insertIntoDBFuture
失败,val f1 = insertIntoDBFuture.flatmap(sendHttpRequestFuture(_))
中的flatMap
不会被执行,所以你不用担心。相反,f1
的结果将是相同的失败未来,因此如果没有发生错误,您将记录 insertIntoDBFuture
或 sendHttpRequestFuture
- 或 none 的错误。
试用此示例。
它显示了所有的工作原理。
事实上,正如我所见,你已经实施得很好。
有两个变量可以修改 job1Fail
和 job2Fail
。
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
object FutFut extends App {
val f1: Future[Int] = Future {
doJob
}
val job1Fail = false
val job2Fail = true
def doJob() = {
if (job1Fail) sys.error("Failed to do job") else {
println("job 1 done")
1
}
}
def doOtherJob() {
if (job2Fail) sys.error("Failed to do other job") else {
println("other job done")
}
}
def revertingAll() {
println("reverting all")
}
f1.flatMap {
x => {
val f2 = Future {
doOtherJob()
}
f2.onFailure {
case t => revertingAll()
}
f2
}
}
f1.onFailure {
case t => println("f1 failed")
}
Thread.sleep(1000)
}
所以结果如下:
db job fail -> output message f1 failed. no revert.
db job ok -> flat map invoked -> http fail -> revert invoked -> f1 does not fail
db job ok -> flat map invoked -> http ok -> all ok -> no revert -> no fail :)
这就是所有用例。
在这种情况下,您可能应该使用 recoverWith
API,它允许您将失败的未来转化为另一个未来:
f2.recoverWith({
case NonFatal(_) => dao.revertDbChangeFuture
})
这将 return 你反对撤消操作的 Future。
我是 scala 的新手,所以要温柔!
我正在尝试创建一个 "logical transaction",这是一个代码示例:
val f1:Future[Int] = dao.insertIntoDB
f1.flatmap{
x => {
val f2 = sendHttpRequestFuture
f.onFailure{
case t => dao.revertDbChangeFuture
}
}
f1.onFailure{
logger.error("error)
}
所以我认为会发生的是先执行外层的onFailure,然后是内层的onFailure。
问题是 onFailure return 单元,所以把 future 放在 onFailure 里面感觉很奇怪,我不确定处理这种用例的正确方法是什么。
我基本上需要还原我的更改,还原操作是异步的。
你能给点建议吗? 谢谢!
你不应该使用 revertDbChangeFuture
,因为一旦你有了那个未来,它通常意味着你已经触发了底层计算,即你已经在恢复数据库更改,无论结果如何的插入。
相反,您应该添加 方法 以在 onFailure
回调中恢复数据库更改:
insertIntoDBFuture.onFailure {
case t => revertDbChange()
}
val f1 = insertIntoDBFuture.flatmap(sendHttpRequestFuture(_))
f1.onFailure{
case t => logger.error("error", t)
}
f1
如果insertIntoDBFuture
失败,val f1 = insertIntoDBFuture.flatmap(sendHttpRequestFuture(_))
中的flatMap
不会被执行,所以你不用担心。相反,f1
的结果将是相同的失败未来,因此如果没有发生错误,您将记录 insertIntoDBFuture
或 sendHttpRequestFuture
- 或 none 的错误。
试用此示例。 它显示了所有的工作原理。 事实上,正如我所见,你已经实施得很好。
有两个变量可以修改 job1Fail
和 job2Fail
。
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
object FutFut extends App {
val f1: Future[Int] = Future {
doJob
}
val job1Fail = false
val job2Fail = true
def doJob() = {
if (job1Fail) sys.error("Failed to do job") else {
println("job 1 done")
1
}
}
def doOtherJob() {
if (job2Fail) sys.error("Failed to do other job") else {
println("other job done")
}
}
def revertingAll() {
println("reverting all")
}
f1.flatMap {
x => {
val f2 = Future {
doOtherJob()
}
f2.onFailure {
case t => revertingAll()
}
f2
}
}
f1.onFailure {
case t => println("f1 failed")
}
Thread.sleep(1000)
}
所以结果如下:
db job fail -> output message f1 failed. no revert.
db job ok -> flat map invoked -> http fail -> revert invoked -> f1 does not fail
db job ok -> flat map invoked -> http ok -> all ok -> no revert -> no fail :)
这就是所有用例。
在这种情况下,您可能应该使用 recoverWith
API,它允许您将失败的未来转化为另一个未来:
f2.recoverWith({
case NonFatal(_) => dao.revertDbChangeFuture
})
这将 return 你反对撤消操作的 Future。