使用Future进行并发搜索和结果返回

时间:2017-07-29 23:10:27

标签: scala

我正在创建一个运行在单词列表中的服务,并且每个单词都会检查数据库中是否存在相关操作(执行)。

我正在尝试使用Futures同时执行此操作,但我不确定我是否使用了最佳方式。

```

class CommandDiscoveryService (commandsDAO: CommandsDAO, text: String) {

  val words = text.split("\\s+")

  var results = new ListBuffer[Option[Execution]]()

  // Temporarily handle with concurrent searchs on the database
  // TODO Load all commands to memory and check the list ?? memcache or some other cache service
  if (words.size <= 6) {
    Logger.debug("Searching for executions with text " + text )
    findExecution()
  }

  def findExecution() = {
    val lb = new ListBuffer[Future[Seq[Execution]]]()
    for (word <- words) {
      lb += commandsDAO.findExecutionByName(word)
    }

    lb.foreach(Await.result(_, 1 seconds))

    import scala.concurrent.ExecutionContext.Implicits.global // FIXME LATER
    val res = lb.map {
      ftr => ftr.map{
        res => {
          if (res.size > 0 ) {
            Logger.debug("RES SIZE:" + res.size)
            res.map{ ex => results += Some(ex) }
          }
        }
      }
    }
  }

  def getExecution(): Option[Execution] = {
    if (results.size > 1 ) {
      Logger.debug("ERROR_TOMANYEXECS: Found more than one execution " + results.head)
      results.foreach{
        execs => Logger.debug("ERROR_TOMANYEXECS: " + execs)
      }

      None
    } else {
      if (results.size == 0 ) {
        Logger.debug("NOTHING FOUND IN RES")
        None
      } else {
        Logger.debug("FOUND RES " + results.head)
        results.head
      }
    }
  }

}

```

当我调用getExecution时,我需要已经获得了搜索完成的值。我不确定是否对这个结果变量进行锁定将是一个解决方案对未来的等待[Seq [执行]]已经没有建议。

PS:我正在使用playframework 2.6.x和Slick来运行它。

1 个答案:

答案 0 :(得分:2)

您的results ListBuffer仅填充了Some[Execution],绝不会使用None,因此使用Option没有任何意义。我建议使用不可变集合并重新定义findExecution方法以返回Future[List[Execution]]

val words = text.split("\\s+").toList

def findExecution: Future[List[Execution]] = {
  val executions = words.map(commandsDAO.findExecutionByName(_)) // List[Future[Seq[Execution]]]
  val filtered = executions.map(_.filter(_.nonEmpty)) // List[Future[Seq[Execution]]
  val flattened = Future.sequence(filtered).map(_.flatten) // Future[List[Execution]]
  flattened
}

findExecution现在返回所有单词或名称的所有Future中的Execution个单词,除了没有任何Execution s的单词。< / p>

  

当我调用getExecution时,我需要已经获得了搜索完成的值

更好的方法是让getExecution同时返回Future

def getExecution: Future[Option[Exception]] = {
  val executions = findExecution // Future[List[Execution]]
  executions.map { e =>
    if (e.size > 1) {
      // ...
      None
    } else if (e.isEmpty) {
      // ...
      None
    } else { // e.size is one
      // ...
      Some(e.head)
    }
  }
}

上述方法避免了阻塞Await调用,并且适合异步Play和Slick API。