Почему именно будущее.firstCompletedOf не вызывать обратный вызов по таймауту?


Я делаю упражнения из обучения параллельному программированию в Scala.

Для вопроса упражнения в комментарии кода.

  1. программа печатает правильный вывод содержимого HTML для правильного URL и таймаута достаточно.
  2. программа печатает "ошибка произошла"для правильного 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
}
2 2

2 ответа:

Как сказал @Gábor Bakos исключение производит 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 => ...}

Полный код после использования recover, как советует @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
}