Future.traverse确保执行顺序

时间:2017-06-13 06:10:45

标签: scala monads

我使用Future.traverse,是保证执行的顺序。必须调用我的函数fn,并且在为下一个元素运行之前完成将来的完成。

val xs = Seq[T] ???
def fn(t: T): Future[Unit] = ??? 
Future.traverse(xs)(fn)

谢谢,

3 个答案:

答案 0 :(得分:2)

在scala 2.11中实现traverse

  def traverse[A, B, M[X] <: TraversableOnce[X]](in: M[A])(fn: A => Future[B])(implicit cbf: CanBuildFrom[M[A], B, M[B]], executor: ExecutionContext): Future[M[B]] =
    in.foldLeft(successful(cbf(in))) { (fr, a) =>
      val fb = fn(a)
      for (r <- fr; b <- fb) yield (r += b)
    }.map(_.result())

val fb = fn(a)创建Future[B],然后只与之前创建的未来for (r <- fr; b <- fb) yield (r += b)合成。所以答案是否定的。没有执行订单保证。

在scala 2.12中实现改变了:

def traverse[A, B, M[X] <: TraversableOnce[X]](in: M[A])(fn: A => Future[B])(implicit cbf: CanBuildFrom[M[A], B, M[B]], executor: ExecutionContext): Future[M[B]] =
    in.foldLeft(successful(cbf(in))) {
      (fr, a) => fr.zipWith(fn(a))(_ += _)
    }.map(_.result())(InternalCallbackExecutor)

但是再次&#39; next&#39; Future之前是创建的(zipWith的第一个参数是&#39;按值调用&#39;)与之前的fr链接。

如果您需要按顺序遍历,那么只需稍微更改2.11实现:

def traverse[A, B, M[X] <: TraversableOnce[X]](in: M[A])(fn: A => Future[B])(implicit cbf: CanBuildFrom[M[A], B, M[B]], executor: ExecutionContext): Future[M[B]] =
    in.foldLeft(successful(cbf(in))) { (fr, a) =>
      for (r <- fr; b <- fn(a)) yield (r += b)
    }.map(_.result())

答案 1 :(得分:2)

正如其他答案已经说明的那样:不,traverse没有(必然[1])对元素按顺序,完成应用转换。

但是,您可以制作相当于linearize

的内容

也许是这样的:

import scala.concurrent._
import scala.collection.mutable.Builder
import scala.collection.generic.CanBuildFrom
import language.higherKinds

/**
 * Linearize asynchronously applies a given function in-order to a sequence of values, producing a Future with the result of the function applications.
 * Execution of subsequent entries will be aborted if an exception is thrown in the application of the function.
 */
def linearize[T, U, C[T] <: Traversable[T]](s: C[T])(f: T => Future[U])(implicit cbf: CanBuildFrom[C[T], U, C[U]], e: ExecutionContext): Future[C[U]] = {
  def next(i: Iterator[T], b: Builder[U, C[U]]): Future[C[U]] =
    if(!i.hasNext) Future.successful(b.result)
    else Future.unit.flatMap(_ => f(i.next()).flatMap(v => next(i, b += v)))
  next(s.toIterator, cbf(s))
}

1:您可以想象一个同步EC实现顺序效果。

答案 2 :(得分:1)

看起来不像我

ScalaDocs 2.12.0

  

使用提供的函数A =&gt;异步且非阻塞地将TraversableOnce [A]转换为Future [TraversableOnce [B]]。未来[B]。这对于执行并行映射非常有用。

文档中没有特别提及,这意味着如果存在更高效的方法,合同可能会发生变化。它还提到了“并行映射”,这是另一个提示,它不太可能保留执行顺序。