2016-11-27 10 views
0

기본 질문에 대해 미리 사과드립니다. http4s에서 스칼라를 배우기 시작했고 라우터 핸들러에서 MongoDB에 항목을 입력하려고합니다. 내가 말할 수있는 한 insertOneObservable[Completed]을 반환합니다.http4s, Service Executor 및 Mongodb : insertOne이 끝날 때까지 기다리는 방법

응답을 반환하기 전에 observalbe가 완료 될 때까지 기다릴 수있는 아이디어가 있습니까?

내 코드입니다 : 내가 https://github.com/haghard/mongo-query-streams을 권 해드립니다

class Routes { 
    val service: HttpService = HttpService { 
     case r @ GET -> Root/"hello" => { 
      val mongoClient: MongoClient = MongoClient() 
      val database: MongoDatabase = mongoClient.getDatabase("scala") 
      val collection: MongoCollection[Document] = database.getCollection("tests") 
      val doc: Document = Document("_id" -> 0, "name" -> "MongoDB", "type" -> "database", 
           "count" -> 1, "info" -> Document("x" -> 203, "y" -> 102)) 
      collection.insertOne(doc) 
      mongoClient.close() 
      Ok("Hello.") 
     } 
    } 
} 

class GomadApp(host: String, port: Int) { 
    private val pool = Executors.newCachedThreadPool() 

    println(s"Starting server on '$host:$port'") 

    val routes = new Routes().service 

    // Add some logging to the service 
    val service: HttpService = routes.local { req => 
    val path = req.uri 
    val start = System.nanoTime() 
    val result = req 
    val time = ((System.nanoTime() - start)/1000)/1000.0 
    println(s"${req.remoteAddr.getOrElse("null")} -> ${req.method}: $path in $time ms") 
    result 
    } 

    // Construct the blaze pipeline. 
    def build(): ServerBuilder = 
    BlazeBuilder 
     .bindHttp(port, host) 
     .mountService(service) 
     .withServiceExecutor(pool) 
} 

object GomadApp extends ServerApp { 
    val ip = "127.0.0.1" 
    val port = envOrNone("HTTP_PORT") map (_.toInt) getOrElse (8787) 

    override def server(args: List[String]): Task[Server] = 
    new GomadApp(ip, port) 
     .build() 
     .start 

} 

답변

0

- 당신이 그것을 포크해야하고 종속성까지 조금, 7.1 및 7.2 바이너리 호환되지 않습니다 scalaz 있지만.

덜-개울 (덜 referentially 올바른) 방법 : https://github.com/Verizon/delorean

collection.insertOne(doc).toFuture().toTask.flatMap({res => Ok("Hello")}) 

후자의 솔루션은 쉽게 보이지만 몇 가지 숨겨진 함정이있다. https://www.reddit.com/r/scala/comments/3zofjl/why_is_future_totally_unusable/

이 트윗은 나에게 놀라운 일을 만들어보기 : https://twitter.com/timperrett/status/684584581048233984 당신이 선물 "완전히 사용할 수 없게"를 고려하거나 단지 과장이 있습니까? 나는 큰 문제는 한번도 겪어 보지 못했지만 나는 기꺼이 받아 들여야한다. 다음 코드는 Futures를 효과적으로 "게으르게"만들지 않습니까? def myFuture = Future {42} 그리고 마침내 나는 scalaz의 Tasks가 실패한 것도 들었지만 그다지 많은 것을 발견하지 못했다. 더 자세한 정보가 있으십니까?

답변 :

근본적인 문제는 사이드 초래 표정으로 미래를 건설하는 것은 부작용이 자체 것입니다. 불행하게도 일반적으로 사용되는 방식이 아닌 순수한 계산을 위해 미래에 대해서만 추론 할 수 있습니다.

import scala.concurrent.Future 
import scala.concurrent.ExecutionContext.Implicits.global 
import scala.util.Random 

val f1 = { 
    val r = new Random(0L) 
    val x = Future(r.nextInt) 
    for { 
    a <- x 
    b <- x 
    } yield (a, b) 
} 

// Same as f1, but I inlined `x` 
val f2 = { 
    val r = new Random(0L) 
    for { 
    a <- Future(r.nextInt) 
    b <- Future(r.nextInt) 
    } yield (a, b) 
} 

f1.onComplete(println) // Success((-1155484576,-1155484576)) 
f2.onComplete(println) // Success((-1155484576,-723955400)) <-- not the same 

그러나이 작업으로 잘 작동 : 여기에 참조 투명성을 깨는이 작업의 데모입니다. 흥미로운 것은 인라인되지 않은 버전으로, 두 개의 별개의 Int 값을 생성합니다. 이것은 중요한 비트입니다 : 작업에는 부작용을 값으로 캡처하는 생성자가 있으며 Future는 그렇지 않습니다.

import scalaz.concurrent.Task 

val task1 = { 
    val r = new Random(0L) 
    val x = Task.delay(r.nextInt) 
    for { 
    a <- x 
    b <- x 
    } yield (a, b) 
} 

// Same as task1, but I inlined `x` 
val task2 = { 
    val r = new Random(0L) 
    for { 
    a <- Task.delay(r.nextInt) 
    b <- Task.delay(r.nextInt) 
    } yield (a, b) 
} 

println(task1.run) // (-1155484576,-723955400) 
println(task2.run) // (-1155484576,-723955400) 

와 "당신이 반복해서 같은 작업을 구성 할 수 있습니다" "당신이 그것을 요청할 때까지 작업이 실행되지 않습니다"와 같은 일반적으로 인용 차이의 대부분이 기본적인 구분으로 추적 . 그래서 "완전히 사용할 수없는"이유는 순수한 가치로 프로그래밍하고 등식 추론을 사용하여 프로그램을 이해하고 조작하는 데 익숙해지면 상황을 이해하기가 훨씬 어려운 부작용의 세계로 되돌리기가 어렵 기 때문입니다.

+0

좋아, 설명서를 읽은 후 확인해 보았습니다. 응답은 작업 그 자체라고 생각됩니다. –

+0

0.15 문서를 읽으십시오. – Reactormonk