ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • mhtml Rx 이해하기 - Ajax 호출시 경험
    web/mhtml 2021. 10. 27. 23:28

    문제

    다음 코드는 유효한 url 을 호출하였음에도 브라우저 콘솔에서 에러 메시지를 볼 수 있다. 

    import mhtml._
    import mhtml.future.syntax.FutureToRxSyntax
    import org.scalajs.dom
    import org.scalajs.dom.ext.Ajax
    
    import scala.concurrent.ExecutionContext.Implicits.global
    import scala.util.Success
    
    object MHtmlTest {
      def view(): Unit = {
        val url: String                = "https://jsonplaceholder.typicode.com/todos/1"
        val response: Rx[List[String]] = Ajax.get(url).toRx.map {
          case Some(Success(xhr)) => List(xhr.responseText)
          case _                  => List.empty[String]
        }
    
        val text: Rx[Option[String]] = response.map(_.headOption)
        val content          = <div>{text}</div>
        mount(dom.document.getElementById("main_content"), content)
      }
    }

    에러 메시지는 다음과 같았다.

    Throwables.scala:18 Uncaught java.util.NoSuchElementException: head of empty list
        at $c_sci_Nil$.head__E (http://localhost:9000/versionedAssets/client-fastopt.js:45271:44)
        at $c_sci_Nil$.head__O (http://localhost:9000/versionedAssets/client-fastopt.js:45300:8)
        at http://localhost:9000/versionedAssets/client-fastopt.js:2879:22
        at $c_sjsr_AnonFunction1.apply__O__O (http://localhost:9000/versionedAssets/client-fastopt.js:19303:43)
        at http://localhost:9000/versionedAssets/client-fastopt.js:7635:34
        at $c_sjsr_AnonFunction1.apply__O__O (http://localhost:9000/versionedAssets/client-fastopt.js:19303:43)
        at http://localhost:9000/versionedAssets/client-fastopt.js:7635:18
        at $c_sjsr_AnonFunction1.apply__O__O (http://localhost:9000/versionedAssets/client-fastopt.js:19303:43)
        at $c_Lmhtml_Var.foreach__F1__F0 (http://localhost:9000/versionedAssets/client-fastopt.js:13400:19)
        at $c_Lmhtml_Rx$.run__Lmhtml_Rx__F1__F0 (http://localhost:9000/versionedAssets/client-fastopt.js:7834:18)

    Why?

     

    설명

    mhtml 의 Rx 는 async 로 toRx 메서드 구현을 살펴보면 아래처럼 되어있다.

    object Utils {
      /** Convert a `Future` to a `Rx` */
      def futureToRx[T](f: Future[T])(implicit ec: ExecutionContext): Rx[Option[Try[T]]] = {
        val result: Var[Option[Try[T]]] = Var(None)
        f.onComplete(x => result := Some(x))
        result
      }
    }
    
    object syntax {
      implicit class FutureToRxSyntax[T](f: Future[T]) {
        def toRx(implicit ec: ExecutionContext): Rx[Option[Try[T]]] =
          Utils.futureToRx(f)
      }
    }

     

    따라서 즉시 Var(None) 을 리턴하고, 계산이 다 되면 이후에 다시 한번 Some(x) 가 리턴되어 dom 을 다시 그리게 된다.

    그런데 작성된 코드에서는 Var(None) 을 리턴 받았을 때 제대로 처리하지 못하게 되어있다.

    val text: Rx[String] = response.map(_.head)

    이 부분을 아래와 같이 고쳐야 한다.

    val text: Rx[Option[String]] = response.map(_.headOption)

    Rx 가 async 로 바로 리턴되고, 이후에 제대로된 응답을 받으면 다시 그린다는 것을 주의해야 한다.

    아니면 dropIf 나 keepIf 함수를 사용해 바로 리턴되는 값은 필터링해 전파되는 것을 막는 것이 좋을 것 같다.

    댓글

Designed by Tistory.