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

Scala 为什么Future.firstCompletedOf在超时时不调用回调?,scala,concurrency,promise,future,Scala,Concurrency,Promise,Future,我正在从学校做练习 有关代码注释中的练习问题 程序输出正确的HTML内容以获得正确的URL和足够的超时 程序为正确的URL和低超时打印“出错” 但是,对于无效URL,不会打印“出错”。下面的代码有什么问题 /* * 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 ti

我正在从学校做练习

有关代码注释中的练习问题

  • 程序输出正确的HTML内容以获得正确的URL和足够的超时
  • 程序为正确的URL和低超时打印“出错”
  • 但是,对于无效URL,不会打印“出错”。下面的代码有什么问题

    /*
     * 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
    }
    

    正如@Gábor Bakos所说,异常会产生
    故障
    ,而map不会处理该故障:

    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
    
    处理故障-使用
    恢复
    方法:

    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 => ...}
    

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

    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
    }
    

    对于无效URL,最有可能的是
    fromURL
    终止时出现
    异常
    ,因此在该分支上没有成功(但失败),
    map
    将不会被调用,
    case=>
    将不匹配。@Gábor Bakos oh yeah得到它。使用Try和it worked将htmlContents更改为此worked
    val htmlContents=Future{url map{x=>val html=Try(来源于url(x)mkString)html匹配{case Success(x)=>x case Failure(error)=>“发生错误:可能是无效的url”}
    为什么不直接尝试/捕获而不是尝试+匹配?您也破坏了您的类型安全,因为现在您无法区分错误和正常结果。因此,使用recover(它使用Try-inside)而不是显式Try是错误的:
    Future.firstCompletedOf(Seq(timeOut2Sec,htmlContents))recover{case x=>println(“Error:+x”);None}map{x=>…}
    ?@dk14。是的,这好多了。以前没有检查recover返回的内容。因此,我已使用此特定解决方案更新了答案-因此,如果您愿意,可以将其标记为right(通过单击复选框)。