Метод onComplete в Fututre monod от Scala

#scala #future

#scala #будущее

Вопрос:

Я учусь Future на Scala, и у меня есть следующий фрагмент кода. Я генерирую случайные метки на основе первой буквы в названии. Для следующего сценария я ожидаю, что список будет напечатан этим onComplete методом. Но он ничего не печатает.

 def randomMark(name:String) = name(0) match {
  case 'R'|'A'|'J'|'S' => Thread.sleep(500); 99
  case  _              => Thread.sleep(500); 80
}

import scala.concurrent._
import concurrent.ExecutionContext.Implicits.global

val returns = Future sequence List( Future(randomMark("Rob")), Future(randomMark("Andy")), Future(randomMark("George")) )
    
Thread.sleep(550)

returns onComplete { e => { val y1 = e.getOrElse("Error"); println(y1) } }    
//This println statement does not execute. I expect a list List(99,99,80) to be printed
 

Может кто-нибудь, пожалуйста, помочь мне понять, почему функциональный литерал, который я предоставляю для onComplete метода, не выполняется?

Спасибо!

Комментарии:

1. Если это целая программа, то она просто завершается, не дожидаясь завершения future. Попробуйте добавить Await.ready(returns, Duration.Inf)

Ответ №1:

Скорее всего, потому, что вам нужно дождаться результата, потому onComplete что это асинхронная операция.

 import scala.concurrent.duration._
import scala.concurrent._

def randomMark(name:String) = name(0) match {
  case 'R'|'A'|'J'|'S' => Thread.sleep(500); 99
  case  _              => Thread.sleep(500); 80
}

import scala.concurrent._
import concurrent.ExecutionContext.Implicits.global

val returns = Future sequence List( Future(randomMark("Rob")), Future(randomMark("Andy")), Future(randomMark("George")) )
    
Thread.sleep(550)

returns onComplete { e => { val y1 = e.getOrElse("Error"); println(y1) } }   
println("Waiting futures to be completed")
Await.ready(returns, 5.seconds)
println("Futures to be completed")
 

Распечатка закончилась:

 Waiting futures to be completed
List(99, 99, 80)
Futures to be completed
 

Scatie: https://scastie.scala-lang.org/SWv18p8RTtuo7nMNHNHMoQ

Комментарии:

1. Спасибо, Иван! Когда я поменял местами последние два оператора в своей программе, я получил ожидаемый результат. Спасибо, что уделили мне время.

Ответ №2:

Поскольку обратные вызовы выполняются асинхронно, вам нужно будет дождаться завершения обратного вызова перед завершением работы программы. В этом случае, если вы хотите дождаться завершения обратного вызова, вам нужно каким-то образом сигнализировать о его завершении. В следующем примере я использую обещание:

 def randomMark(name:String) = name(0) match {
  case 'R'|'A'|'J'|'S' => Thread.sleep(500); 99
  case  _              => Thread.sleep(500); 80
}

import scala.concurrent._
import scala.concurrent.duration._
import ExecutionContext.Implicits.global

val returns = Future sequence List( Future(randomMark("Rob")), Future(randomMark("Andy")), Future(randomMark("George")) )
val returns: scala.concurrent.Future[List[Int]] = Future(<not completed>)

val p = Promise[Unit]()

returns onComplete { e => { val y1 = e.getOrElse("Error"); println(y1); p.success(()) } }

Await.ready(p.future, 5.seconds)
 

Но вы могли бы вместо этого использовать andThen следующее:

 def randomMark(name:String) = name(0) match {
  case 'R'|'A'|'J'|'S' => Thread.sleep(500); 99
  case  _              => Thread.sleep(500); 80
}

import scala.concurrent._
import scala.concurrent.duration._
import ExecutionContext.Implicits.global

val returns = 
  Future sequence List( Future(randomMark("Rob")), Future(randomMark("Andy")), Future(randomMark("George")) ) andThen
  { case e => { val y1 = e.getOrElse("Error"); println(y1) } }

Await.ready(returns, 5.seconds)
 

Комментарии:

1. Спасибо за подробное объяснение, Виктор! Я не понял, что позиции последних двух операторов в моем примере. Когда я поменял их местами, я получил результат.