对于包含Scala Futures的循环修改List

时间:2017-06-08 20:51:58

标签: scala concurrency future

假设我有一个ListBuffer[Int],我用foreach循环迭代它,每个循环将从Future内部修改此列表(删除当前元素),并且列表为空时执行一些特殊操作。示例代码:

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.collection.mutable.ListBuffer

val l = ListBuffer(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
l.foreach(n => Future {
  println(s"Processing $n")
  Future {
    l -= n
    println(s"Removed $n")
    if (l.isEmpty) println("List is empty!")
  }
})

这可能会非常糟糕。我有一个更复杂的代码,具有相似的结构和相同的需求,但我不知道如何构造它,所以我可以更可靠的方式实现相同的功能。

1 个答案:

答案 0 :(得分:0)

你提出问题的方式实际上并不属于scala所针对的功能范例。

你似乎想要的是做一个异步计算列表,在每个计算结束时做一些事情,在每个计算结束时做一些事情。如果您使用continuation,这非常简单,使用map上的flatMapFuture方法很容易实现。

val fa: Future[Int] = Future { 1 } 
// will apply the function to the result when it becomes available
val fb: Future[Int] = fa.map(a => a + 1) 
// will start the asynchronous computation using the result when it will become available
val fc: Future[Int] = fa.flatMap(a => Future { a + 2 }) 

完成所有这些操作后,您可以在每个Future完成(成功)后轻松执行某些操作:

val myFutures: List[Future[Int]] = ???
myFutures.map(futInt => futInt.map(int => int + 2))

在这里,我将从List中的不同异步计算中为每个值添加2。

您还可以选择使用Future等待列表中的所有Future.sequence完成:

val myFutureList: Future[List[Int]] = Future.sequence(myFutures)

再一次,您获得Future,当输入列表中的每个Future成功解析后,将会解析,或者只要您Future个失败。然后,您就可以在此新map上使用flatMapFuture,一次性使用所有计算值。

所以我在这里编写你建议的代码:

val l = 1 to 10

val processings: Seq[Future[Unit]] = l.map {n =>
  Future(println(s"processing $n")).map {_ => 
    println(s"finished processing $n")
  }
}

val processingOver: Future[Unit] =
  Future.sequence(processings).map { (lu: Seq[Unit]) => 
    println(s"Finished processing ${lu.size} elements")
  }

当然,我建议使用真正的函数而不是程序(返回Unit),以便您可以使用值来执行某些操作。我使用println来生成一个代码,它会产生与你相同的输出(除了印刷品,其含义略有不同,因为我们不再改变任何东西)。