为什么Future.firstCompletedOf在超时时不调用回调?

时间:2015-01-11 06:57:53

标签: scala concurrency promise future

我正在从Learning Concurrent Programming in Scala进行练习。

对于代码注释中的练习题。

  1. 程序打印HTML内容的正确输出以获得正确的URL并足够超时。
  2. 程序打印"发生错误"正确的URL和低超时。
  3. 但是对于无效的网址"发生了错误"没有打印。下面的代码有什么问题?

    /*
     * Implement a command-line program that asks the user to input a URL of some website, 
     * and displays the HTML of that website. Between the time that the user hits ENTER and 
     * the time that the HTML is retrieved, the program should repetitively print a . to the 
     * standard output every 50 milliseconds, with a two seconds timeout. Use only futures 
     * and promises, and avoid the synchronization primitives from the previous chapters. 
     * You may reuse the timeout method defined in this chapter.
     */
    object Excersices extends App {
    
      val timer = new Timer()
    
      def timeout(t: Long = 1000): Future[Unit] = {
        val p = Promise[Unit]
        val timer = new Timer(true)
        timer.schedule(new TimerTask() {
          override def run() = {
            p success ()
            timer cancel()
          }
        }, t)
        p future
      }
    
      def printDot = println(".")
    
      val taskOfPrintingDot = new TimerTask {
        override def run() = printDot
      }
    
      println("Enter a URL")
      val lines = io.Source.stdin.getLines()
      val url = if (lines hasNext) Some(lines next) else None
    
      timer.schedule(taskOfPrintingDot, 0L, 50.millisecond.toMillis)
      val timeOut2Sec = timeout(2.second.toMillis)
    
      val htmlContents = Future {
        url map { x =>
          blocking {
            Source fromURL (x) mkString
          }
        }
      }
    
      Future.firstCompletedOf(Seq(timeOut2Sec, htmlContents)) map { x =>
        timer cancel ()
        x match {
          case Some(x) =>
            println(x)
          case _ =>
            println("Error occured")
        }
    
      }
    
      Thread sleep 5000
    }
    

2 个答案:

答案 0 :(得分:1)

正如@GáborBakos所说,异常会产生Failure而不是由地图处理:

val fut = Future { Some(Source fromURL ("hhhttp://google.com")) }

scala> fut  map { x => println(x) } //nothing printed
res12: scala.concurrent.Future[Unit] = scala.concurrent.impl.Promise$DefaultPromise@5e025724

要处理失败 - 请使用recover方法:

scala> fut recover { case failure => None } map { x => println(x) } 
None
res13: scala.concurrent.Future[Unit] = scala.concurrent.impl.Promise$DefaultPromise@578afc83

在您的上下文中,它类似于:

Future.firstCompletedOf(Seq(timeOut2Sec, htmlContents)) recover {case x => println("Error:" + x); None} map { x => ...}

答案 1 :(得分:0)

使用@ dk14建议的恢复后的完整代码:

object Exercises extends App {

  val timer = new Timer()
  def timeout(t: Long = 1000): Future[Unit] = {
    val p = Promise[Unit]
    val timer = new Timer(true)
    timer.schedule(new TimerTask() {
      override def run() = {
        p success ()
        timer cancel ()
      }
    }, t)
    p future
  }

  def printDot = println(".")

  val taskOfPrintingDot = new TimerTask {
    override def run() = {
      printDot
    }
  }

  println("Enter a URL")
  val lines = io.Source.stdin.getLines()
  val url = if (lines hasNext) Some(lines next) else None

  timer.schedule(taskOfPrintingDot, 0L, 50.millisecond.toMillis)
  val timeOut2Sec = timeout(2.second.toMillis)

  val htmlContents = Future {
    url map { x =>
      blocking {
        Source fromURL (x) mkString
      }
    }
  }

  Future.firstCompletedOf(Seq(timeOut2Sec, htmlContents))
    .recover { case x => println("Error:" + x); None }
    .map { x =>
      timer cancel ()
      x match {
        case Some(x) =>
          println(x)
        case _ =>
          println("Timeout occurred")
      }
    }

  Thread sleep 5000
}