콘텐츠로 이동

2025 02 21

2025-02-21

Future andThen

  • 원래의 Future가 완료된 뒤, 부수효과 실행을 위해 콜백 실행
    • 부수효과 정상 동작: 콜백이 완료되면, 기존의 결과 유지
    • 부수효과 예외 동작: 예외가 최종 결과로 대체
  • Play 비동기 처리
    1. 라우팅 처리 : Future[Result]를 액션에 반환
    2. 비동기 액션 실행
    3. Future 내에서 예외 발생 및 전파
    4. Play의 글로벌 에러 핸들러 호출
    5. 에러 핸들러에 의한 처리 - 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로 보고!
    • 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")))
      }