2025 02 21
2025-02-21¶
Future andThen¶
- 원래의 Future가 완료된 뒤, 부수효과 실행을 위해 콜백 실행
- 부수효과 정상 동작: 콜백이 완료되면, 기존의 결과 유지
- 부수효과 예외 동작: 예외가 최종 결과로 대체
- Play 비동기 처리
- 라우팅 처리 : Future[Result]를 액션에 반환
- 비동기 액션 실행
- Future 내에서 예외 발생 및 전파
- Play의 글로벌 에러 핸들러 호출
- 에러 핸들러에 의한 처리 - Play Framework는 액션 메서드가 반환한 Future[Result]가 완료될 때까지, 즉 이벤트 루프가 해당 Future의 완료를 기다리다가 콜백을 실행하는 구조로 동작
- Scala에서의 예시
import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global val test = Future { val i = 10 throw new Exception("test") } val afterCallback = Future(1).andThen(x => throw new Exception("callback")) Thread.sleep(1000) println(test) println(afterCallback) /* * java.lang.Exception: callback at Playground$$anon$1.applyOrElse(main.scala:12) at Playground$$anon$1.applyOrElse(main.scala:12) at scala.concurrent.Future.$anonfun$andThen$1(Future.scala:506) at scala.concurrent.impl.Promise$Transformation.run(Promise.scala:475) at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1395) at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373) at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182) at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655) at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622) at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165) Future(Failure(java.lang.Exception: test)) Future(Success(1)) * */
- Play에서의 예시
- 콜백에서 실패했더라도 예외처리.
- 테스트에서는 getTest2() 말고는 모두 예외 발생인줄 알았으나...
- getTest5()
- 함수 리터럴 대신 표현식이라, 평가 시점에 예외가 발생하여 Future 체인이 구성되지 않고 즉시 실패!!!!!!
- getTest4()
- 이건 정상적으로 동작. Future(1)으로 잘 처리되고, 정상 응답
- 예외 던진거는 ExecutionContext에 reportFailure로 보고!
- getTest5()
- controller에서 map 후속 처리 진행하려고 할 때, Future 실패 상태가 되어 예외 전파
def getTest2() = Action.async { Future(1).map(_ => Ok(Json.obj("code" -> "0000"))) } def getTest3() = Action.async { val test = Future(throw new Exception("test3")) test.map(_ => Ok(Json.obj("code" -> "0000"))) } def getTest4() = Action.async { Future(1).andThen { case _ => throw new Exception("test4") }.map(_ => Ok(Json.obj("code" -> "0000"))) } def getTest5() = Action.async { val test = Future(1).andThen(throw new Exception("test5")) test.map(_ => Ok(Json.obj("code" -> "0000"))) }