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
/*
* 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更改为此workedval 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(通过单击复选框)。