0

提前为基本问题道歉。我开始使用 http4s 学习 Scala,并且在路由器处理程序中,我正在尝试输入 MongoDB 的条目。据我所知,insertOne返回一个Observable[Completed].

知道如何在返回响应之前等待观察完成吗?

我的代码是:

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

}
4

1 回答 1

0

我推荐https://github.com/haghard/mongo-query-streams - 虽然你必须分叉它并增加一些依赖项,但 scalaz 7.1 和 7.2 不是二进制兼容的。

不那么流畅(并且不那么正确)的方式: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 的任务也有一些失败,但我没有找到太多关于它的信息。有人有更多细节吗?

回答:

根本问题是用副作用表达式构造 Future 本身就是副作用。您只能将 Future 用于纯粹的计算,不幸的是,这不是它们的常用方法。以下是此操作破坏引用透明度的演示:

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

但是,这适用于 Task。请注意,有趣的是非内联版本,它设法产生两个不同的 Int 值。这是重要的一点:Task 有一个将副作用捕获为值的构造函数,而 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)

大多数常见的差异,如“一个任务在你要求它之前不会运行”和“你可以一遍又一遍地编写同一个任务”,都可以追溯到这个基本区别。因此,它“完全无法使用”的原因是,一旦您习惯于使用纯值进行编程并依靠等式推理来理解和操作程序,就很难回到事情更难理解的副作用世界。

于 2016-11-27T10:05:18.833 回答