Scala

Scala의 Future의 map, for, recover

partner_jun 2017. 4. 16. 11:08

1. map, filter

언젠가 값을 가지게 되는 Future에 고차함수를 적용하여 새로운 Future를 얻을 수 있다.

import scala.concurrent.ExecutionContext.Implicits._

val future: Future[Int] = Future { // 2초 후 값 1을 가지게 된다.
TimeUnit.SECONDS.sleep(2L)
1
}

val futureMap: Future[Int] = future.map(_ * 2)
// future가 완료되면 거기에 2를 곱한다

val futureFilter: Future[Int] = futureMap.filter(_ < 1)

// futureMap이 완료되면 그 중 1보다 작은 값만을 골라낸다.




2. for

스칼라 어디에든 쓰이는 for문도 사용할 수 있다. 

val future: Future[Int] = Future { // 2초 후 값 1을 가지게 된다.
TimeUnit.SECONDS.sleep(2L)
2
}

val result: Future[Int] = for {f <- future
f2 = f * 10 } yield f2

result.onComplete{
case Success(x) => println(x) // 20
}


일반 콜렉션처럼 for문을 이용해 Future 여러개를 묶어 결과를 얻을 수도 있다.

val future: Future[Int] = Future { // 2초 후 값 1을 가지게 된다.
TimeUnit.SECONDS.sleep(2L)
2
}

val future2: Future[Int] = Future { // 1초 후 값 3을 가지게 된다.
TimeUnit.SECONDS.sleep(1L)
3
}

val result: Future[Int] = for {f <- future
f2 <- future2
r = f * f2} yield r

result.onComplete {
case Success(x) => println(x) // 6
}




3. recover

고차함수나 for 모두 새로운 Future를 반환하므로 결국 실제 값을 구하기 위해서는 onComplete 메소드에 부분 함수를 작성하거나 Await.Result 함수를 이용해 블로킹하고 값을 기다려야 한다. Await.Result 함수를 이용하면 Future가 실패해 None 값이 발생할 때 실패 원인에 따른 예외가 발생한다.

futureFilter.onComplete { // 값이 없으므로 None이 출력된다.
case Success(x) => println(x)
case Failure(_) => println("None")
}
/* filter에 해당하는 값이 없어 java.util.NoSuchElementException이 발생한다 */
val result: Int = Await.result(futureFilter, Duration.Inf)
println(result)


None일 때 Exception에 따라 값을 반환해 복구하는 recover 함수를 사용하면 Await.result 함수로도 값을 얻어 낼 수 있다.

val futureFilterRecover: Future[Int] = futureFilter.recover {
case e: java.util.NoSuchElementException => -1
case _ => 0
}

val result: Int = Await.result(futureFilterRecover, Duration.Inf)
println(result) // -1

recover 함수와 비슷하지만 값을 반환하는 대신 새로운 Future를 반환해 작업을 이어가는 recoverWith 함수도 있다.

val future: Future[Int] = Future { // 2초 후 값 1을 가지게 된다.
TimeUnit.SECONDS.sleep(2L)
2
}

val future2: Future[Int] = Future { // 1초 후 값 3을 가지게 된다.
TimeUnit.SECONDS.sleep(1L)
3
}

val futureFiltered: Future[Int] = future.filter(_ > 10) // None (NoSuchElementException)

val futureRecoverWith: Future[Int] = futureFiltered.recoverWith{
case e:NoSuchElementException =>
println("recover")
future2
}

val result = Await.result(futureRecoverWith, Duration.Inf)
println(result)

/*
recover
3
*/