web/mhtml
mhtml Rx 이해하기 - Ajax 호출시 경험
wefree
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 함수를 사용해 바로 리턴되는 값은 필터링해 전파되는 것을 막는 것이 좋을 것 같다.