摆脱Scala未来的嵌套 [英] Get rid of Scala Future nesting

查看:112
本文介绍了摆脱Scala未来的嵌套的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

再一次,当某个函数依赖于某些将来的结果时,我一直在挣扎. 通常可以归结为类似Future [Seq [Future [MyObject]]]

Again and again I am struggling when a function relies on some future results. This usually boils down to a result like Future[Seq[Future[MyObject]]]

要摆脱这一点,我现在在辅助函数中使用Await来取出非未来对象并减少嵌套.

To get rid of that I now use Await inside a helper function to get a non-future object out and reduce the nesting.

看起来像这样

def findAll(page: Int, perPage: Int): Future[Seq[Idea]] = {
    val ideas: Future[Seq[Idea]] = collection.find(Json.obj())
    // [...]

    ideas.map(_.map { // UGLY?
      idea => {
        // THIS RETURNED A Future[JsObject] before
        val shortInfo: JsObject = UserDao.getShortInfo(idea.user_id)
        idea.copy(user_data = Some(shortInfo))
      }
    })
}

此代码有效,但对我来说看起来很hacky.这两个地图调用是另一个缺陷. 我花了几个小时试图弄清楚如何保持这种完全异步并返回一个简单的将来的Seq.如何使用Play2最佳做法解决此问题?

This code works but to me it looks quite hacky. The two map calls are another flaw. I spent hours trying to figure out how to keep this completely asynchronous and returning a simple future Seq. How can this be solved using Play2 best practices?

修改 为了使用例更清楚:

Edit To make the usecase more clear:

我有一个来自mongodb(reactivemongo)的对象A,想要添加来自另一个调用的信息,该信息来自mongodb getShortInfo.这是一个经典的为该帖子招募用户"案例,可以通过在RDBMS中进行联接来解决. getShortInfo自然会由于调用db而产生Future. 为了减少findAll中的嵌套,我使用了Await().这是个好主意吗?

I have an object A from mongodb (reactivemongo) and want to add information coming from another call to mongodb getShortInfo. It's a classical "get user for this post" case that would be solved with a join in RDBMS. getShortInfo naturally would produce a Future because of the call to the db. To reduce the nesting within findAll I used Await(). Is this a good idea?

findAll是通过异步Play动作调用的,转换为Json并通过网络发送.

findAll is called from an asynchronous Play action, converted into Json and sent over the wire.

def getIdeas(page: Int, perPage: Int) = Action.async {

  for {
    count <- IdeaDao.count
    ideas <- IdeaDao.findAll(page, perPage)
  } yield {
    Ok(Json.toJson(ideas))
  }
}    

所以我认为从findAll返回Seq[Future[X]]不会带来更好的性能,因为无论如何我都必须等待结果.这是正确的吗?

So I think returning a Seq[Future[X]] from findAll won't bring better performance as I have to wait for the result anyways. Is this correct?

简而言之: 进行一个Future调用,返回一个Sequence,使用结果的每个元素创建另一个Future调用,以不发生阻塞情况的方式将结果返回到异步操作.

The usecase in short: Take a Future call returning a Sequence, use each element of the result to create another Future call, return the result to an asynchronous action in a way that no blocking situations should occur.

推荐答案

您应该知道,Future随播对象上的两个方便的函数在这里可能会有所帮助,第一个并且更容易缠住头的是Future.sequence.它采用期货序列,并返回序列的期货.如果以Future[Seq[Future[MyObject]]]结尾,则将其称为result.那么您可以使用result.map(Future.sequence(_))

Two handy functions on the Future companion object you should know could help here, the first, and easier to wrap your head around is Future.sequence. It takes a sequnce of futures and returns a Future of a sequence. If are ending up with a Future[Seq[Future[MyObject]]], lets call that result. then you can change this to a Future[Future[Seq[MyObject]]] with result.map(Future.sequence(_))

然后折叠任何X的Future[Future[X]],您可以运行"result.flatMap(identity)",实际上,只要M,您就可以对任何M[M[X]]执行此操作以创建M[X]flatMap.

Then to collapse a Future[Future[X]] for any X, you can run "result.flatMap(identity)", in fact, you can do this for any M[M[X]] to create a M[X] as long as M has flatMap.

此处另一个有用的功能是Future.traverse.基本上,这是获取Seq[A]并将其映射到Seq[Future[B]]然后运行Future.sequence以获取Future[Seq[B]]的结果,因此在您的示例中,您需要:

Another useful function here is Future.traverse. It is basically the result of taking a Seq[A], mapping it to a Seq[Future[B]], then running Future.sequence to get a Future[Seq[B]] So in your example, you'd have:

ideas.map{ Future.traverse(_){ idea =>
    /*something that returns a Future[JsObject]*/
} }.flatMap(identity)

但是,很多时候您在运行flatMap(identity)时,可能会将地图转换为flatMap,在这种情况下就是这样:

However, many times when you are running flatMap(identity), you could be turning a map into a flatMap, and this is the case here:

ideas.flatMap{ Future.traverse(_) { idea =>
    /*something that returns a Future[JsOjbect]*/
} }

这篇关于摆脱Scala未来的嵌套的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆