将来的onComplete回调不被调用

时间:2019-10-16 06:46:30

标签: scala playframework

我正在尝试记录每个任务的经过时间,如下所示。

我遇到的问题是 logElapsedTime 中的回调方法由于某种原因永远不会被调用。

仅调用对Future f 的最后一个回调。

如何解决此问题,以便正确记录每个经过的时间?

    def logElapsedTime[T](f: Future[T], description: String): Future[T] = {
      val start = System.currentTimeMillis()
      f onComplete (_ => logger.debug(s"$description took [${System.currentTimeMillis() - start}]"))
      f
    }

    val f = for {
      _ <- logElapsedTime(task1(), "1st task to be executed")
      result <- logElapsedTime(task2(), "2nd task to be executed")
      _ <- logElapsedTime(task3(), "3rd task to be executed")
      _ <- logElapsedTime(task4(), "4th task to be executed")
    } yield result

    f onComplete {
      case Success(v) =>
        logger.info(s"tasks succeeded !!!! $v")
      case Failure(ex) =>
        logger.error(ex.getMessage)
        throw ex
    }   

输出样本↓

成功时:

  

任务成功!!!!一些价值

失败时:

  

一些错误消息

不记录其他任何输入。
(日志级别设置为debug及以上)

2 个答案:

答案 0 :(得分:3)

您的逻辑没有错。我建议您尝试一下的修改很少。

import org.slf4j.LoggerFactory

import scala.concurrent.Future
import concurrent.ExecutionContext.Implicits.global
import scala.io.StdIn
import scala.util.{Failure, Success}

object FutureOnComplete extends App {

  private val logger = LoggerFactory.getLogger("test")

  def logElapsedTime[T](f: => Future[T], description: String): Future[T] = {
    val start = System.currentTimeMillis()
    f.onComplete(
      _ =>
        logger.warn(
          s"$description took [${System.currentTimeMillis() - start}]"))
    f
  }

  val f = for {
    _ <- logElapsedTime(Future(1), "1st task to be executed")
    result <- logElapsedTime(Future(2), "2nd task to be executed")
    _ <- logElapsedTime(Future(2), "3rd task to be executed")
    _ <- logElapsedTime(Future(2), "4th task to be executed")
  } yield result

  f.onComplete {
    case Success(v) =>
      logger.info(s"tasks succeeded !!!! $v")
    case Failure(ex) =>
      logger.error(ex.getMessage)
      throw ex
  }

  StdIn.readLine()

}
  • 将日志级别提高到warn,以确保不会归咎于您的日志记录。或将其替换为println
  • 例如,在主线程中等待StdIn.readLine()的将来完成。这样可以使异步进程完成并运行onComplete。
  • 使用=> Future[T]的名称参数在方法logElapsedTime中开始执行future。这只是在未来开始时发生变化,而不是日志记录的逻辑

答案 1 :(得分:3)

例如,当我们只想作为副作用执行日志记录而不转换Future内的值时,请考虑andThen

object futureAndThenLogging extends App with LazyLogging {

  def logElapsedTime[T](f: Future[T], description: String): Future[T] = {
    val start = System.currentTimeMillis()
    f andThen { case _ => logger.debug(s"$description took [${System.currentTimeMillis() - start}]") }
  }

  def task1() = Future(1)
  def task2() = Future(2)
  def task3() = Future(3)
  def task4() = Future(4)

  (for {
    _ <- logElapsedTime(task1(), "1st task to be executed")
    result <- logElapsedTime(task2(), "2nd task to be executed")
    _ <- logElapsedTime(task3(), "3rd task to be executed")
    _ <- logElapsedTime(task4(), "4th task to be executed")
  } yield result)
    .andThen {
      case Success(v) => logger.info(s"tasks succeeded !!!! $v")
      case Failure(ex) => logger.error(ex.getMessage)
    }

  Thread.sleep(1000) // just for demonstration purposes
}

请注意,我们不必重新throw ex中的case Failure(ex) => logger.error(ex.getMessage)