2014-10-29 9 views
1

일련의 Future를 순서대로 실행하는 가장 정교한 방법을 찾으려합니다. 하나의 Future의 실행은 이전에 따라 달라집니다. 나는 미래의 임의의 숫자에 대해 이것을하려고 노력하고있다.스칼라 - 임의의 수의 선물을 순차적이지만 의존적으로 실행합니다.

사용자의 경우 :

  • 내 데이터베이스에서 ID의 수를 검색했다.
  • 이제 웹 서비스에서 관련 데이터를 검색해야합니다.
  • 유효한 결과를 찾았 으면 한 번 멈추고 싶습니다.
  • 나는 성공한 결과에만 관심이 있습니다.

이들 모두를 병렬로 실행 한 다음 반환 된 결과 컬렉션을 구문 분석하는 것은 옵션이 아닙니다. 한 번에 하나의 요청을 수행해야하며 이전 요청에서 결과가 반환되지 않는 경우에만 다음 요청을 실행해야합니다.

현재 해결책은이 줄을 따라 있습니다. foldLeft를 사용하여 요청을 실행 한 다음 이전 미래가 특정 조건을 충족시키는 경우에만 다음 미래를 평가합니다.

def dblFuture(i: Int) = { i * 2 } 
val list = List(1,2,3,4,5) 
val future = list.foldLeft(Future(0)) { 
    (previousFuture, next) => { 
    for { 
     previousResult <- previousFuture 
     nextFuture <- { if (previousResult <= 4) dblFuture(next) else previousFuture } 
    } yield (nextFuture) 
    } 
} 

이의 큰 단점은 내가 행복 그리고 난 난 후 그 결과를 찾았 으면 B), 나는 계속 그 결과를 가지고도 일단 내가 모든 항목을 계속 처리)가있다 술어를 평가합니다. 이 경우에는 간단하지만 사실 더 복잡 할 수 있습니다.

나는 이것에 훨씬 더 우아한 해결책을 놓치고있는 것처럼 느낀다.

+0

이 보이지 않기 때문에 내가 사용 사례에 의해 혼란 스러워요로 변환 할 수 있습니다 이전 Future의 결과가 비어있는 경우에만 다음 Future를 실행하기 때문에 설명하는 데이터 흐름 종속성 ("One Future의 실행은 이전에 따라 달라집니다")과 같은 일종의 데이터 흐름 종속성이 있습니다. 내가 뭘 놓치고 있니? 즉, 다음 미래는 단순히 실행 여부를 결정하는 것 이외의 방법으로 이전 결과에 의존합니까? –

+0

이것은 http : // stackoverflow와 매우 비슷합니다.com/questions/26438991/is-there-sequential-future-find/26439838 # 26439838 (내 대답 참조) 및 http://stackoverflow.com/questions/26349318/how-to-invoke-a-method-again- and-again-until-returns-a-future-value-contains –

+0

@ChrisMartin 다음 미래의 실행은 이전 Future가 성공했는지 여부뿐만 아니라 응답에도 의존합니다. 예 : 이전 상태에 404 상태의 WSResponse가 있으면 다음 미래를 실행하고 그렇지 않으면 실행하지 않습니다. – healsjnr

답변

5

예제를 보면 이전 결과가 후속 결과에 영향을주지 않는 것처럼 보입니다. 그 대신 중요한 것은 이전 결과가 다음 결과가 계산되지 않도록 일부 조건을 충족한다는 것입니다. 이 경우 filterrecoverWith을 사용하는 재귀 적 솔루션이 있습니다.

def untilFirstSuccess[A, B](f: A => Future[B])(condition: B => Boolean)(list: List[A]): Future[B] = { 
    list match { 
     case head :: tail => f(head).filter(condition).recoverWith { case _: Throwable => untilFirstSuccess(f)(condition)(tail) } 
     case Nil => Future.failed(new Exception("All failed..")) 
    } 
} 

filterFuture가 완료되면에만 호출 될 것이며, Future가 실패한 경우 recoverWith에만 호출됩니다. 당신은 "미래의 결과"에 대한 추상화 스칼라 미래에서 scalaz.concurrent.Task로 전환해야하지만)

def dblFuture(i: Int): Future[Int] = Future { 
    println("Executing.. " + i) 
    i * 2 
} 

val list = List(1, 2, 3, 4, 5) 

scala> untilFirstSuccess(dblFuture)(_ > 6)(list) 
Executing.. 1 
Executing.. 2 
Executing.. 3 
Executing.. 4 
res1: scala.concurrent.Future[Int] = [email protected] 

scala> res1.value 
res2: Option[scala.util.Try[Int]] = Some(Success(8)) 
+0

대단히 고마워요! – Ikrom

2

산뜻한 방법, 그리고 "사실 함수형 프로그래밍은"scalaz 스트림입니다. 조금 다릅니다. 작업은 순수하고 Future는 "계산 실행 중"이지만 공통점은 많습니다.

import scalaz.concurrent.Task 
    import scalaz.stream.Process 

    def dblTask(i: Int) = Task { 
    println(s"Executing task $i") 
    i * 2 
    } 

    val list = Seq(1,2,3,4,5) 

    val p: Process[Task, Int] = Process.emitAll(list) 

    val result: Task[Option[Int]] = 
    p.flatMap(i => Process.eval(dblTask(i))).takeWhile(_ < 10).runLast 

    println(s"result = ${result.run}") 

결과 :

Executing task 1 
Executing task 2 
Executing task 3 
Executing task 4 
Executing task 5 
result = Some(8) 

귀하의 계산은 이미 미래 스칼라, 당신은 작업

implicit class Transformer[+T](fut: => SFuture[T]) { 
    def toTask(implicit ec: scala.concurrent.ExecutionContext): Task[T] = { 
    import scala.util.{Failure, Success} 
    import scalaz.syntax.either._ 
    Task.async { 
     register => 
     fut.onComplete { 
      case Success(v) => register(v.right) 
      case Failure(ex) => register(ex.left) 
     } 
    } 
    } 
} 
+0

감사합니다 유진, 나는이 솔루션을 많이 좋아하지만 지금은 스칼라를 사용하지 않고 표준 스칼라의 미래를 사용하는 솔루션을 원하고 있습니다 (따라서 왜 @LimbSoup를 올바른 답으로 표시했는지). SacalZ를 좀 더 자세히 살펴볼 것입니다. – healsjnr