2016-11-13 5 views
0

Я пытаюсь обернуть мою голову вокруг Scala, и я задаюсь вопросом, что следующий делает:Scala Futures в Для Понимания

val fFuture: Future[Int] = Future { println("f called"); 3 }                              
val gFuture: Future[Int] = Future { println("g called"); 4 }                              

for { 
    f <- fFuture 
    g <- gFuture 
} yield f 

фьючерсов выполняется внутри из для понимания, правильно? Итак, что делает f в отчете о выходе? Означает ли это, что это доступно? Это считается return value, если это было внутри функции?

+2

«Будущее» не выполняется внутри 'for'. Они выполняются, когда объявляются (всегда), и создаются с помощью 'for'. – cchantep

+1

@cchantep Технически они выполняются только тогда, когда ExecutionContext решает их выполнить. –

+0

Правильно, я должен был сказать, выполненный в свое время по решению ЕС, поскольку их объявления, возможно, (что важно там) между декларацией и перед 'for', без предсказуемого порядка, поскольку эти« Будущие »не зависят друг на друга. – cchantep

ответ

3

фьючерсы начинают выполнять здесь:

val fFuture: Future[Int] = Future { println("f called"); 3 }                              
val gFuture: Future[Int] = Future { println("g called"); 4 } 

Таким образом, обе казни начинают параллельно. Однако, если вы случайно положили Future{...} внутрь для понимания, они будут исполняться последовательно.

Для понимания в основном подписывается и объединяет два результата в одно Будущее. Однако в вашем случае это похоже на то, что результат второго будущего был проигнорирован, что не имеет смысла. Код, который имеет смысл:

for { 
    f <- fFuture 
    g <- gFuture 
} yield f + g 

Этот код возвращает Future[Int] (такой же, как код в вашем примере). Если вы извлечете ценность из этого будущего, вы получите 3 + 4 = 7. Однако это еще не самый лучший подход, как ваши вычисления независимы и вероятность ошибки разработчика (указано выше), что делает их последовательным по-прежнему высок, поэтому рекомендуемый подход для независимых вычислений является:

(fFuture zip gFuture) map { 
    case (f, g) => f + g 
} 

Этот код референциально прозрачным это означает, что даже если заменить fFuture с Future{...} - он по-прежнему ведет себя так же (в случае Future -Они будет выполнен в prallel, но это может быть различным для других параллельности примитивов)

Где бы for-comprehension на самом деле имеет смысл ?Здесь:

for { 
    f <- Future{... 9} 
    g <- if (f > 0) Future{...} else Future{...} 
} yield g 

Как g зависит от f здесь - нет никакого способа, чтобы запустить те параллельно, так for обеспечивает нелипкий способом составления несколько Future сек

-2

Здесь, в вашем сценарии, вам не нужно использовать его для понимания, вы можете просто использовать OnComplete.

ffuture.OnComplete { 
Case Success(result) => println(s"$result') 
Case Failure(ex) => ex.printStackTrace 
} 

Для Понимания необходима, когда у вас есть будущее (ы) в зависимости от другого будущего (ы) как:

var result = for { 
     f <- fFuture 
     g <- f 
    } yield g 

Здесь г будущее будет решен после того, как е завершается, и выход вернется каков результат, который вы возвращаете. В этом случае это будет Future [Int]. С выходом вы можете делать такие вещи.

yield g+f 

Для чтения результата вы можете просто использовать

result.onSuccess or onComplete 

для фьючерсов, я нашел эту статью, как лучший друг: http://alvinalexander.com/scala/concurrency-with-scala-futures-tutorials-examples

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

+0

Было бы очень полезно, если бы вы могли объяснить причину неприязни, я узнаю что-то новое. – user2056463

1

Scala Futures оценивается с нетерпением, которые означает, что их значение сразу вычисляется в отдельном потоке.

Когда вы запускаете операции, зависящие от результата будущего, текущий поток блокируется до тех пор, пока он не будет оценен. Если вы преобразовываете Фьючерсы с использованием методов комбинатора, таких как map или flatMap, вы получаете другое Будущее, которое представляет конечный результат (код, выполняющий эти операции, не нужно блокировать).

Так что в вашем примере для понимания является обессахаренным компилятором к следующему выражению:

fFuture.flatMap(f => gFuture.map(g => f + g)) 

Что само по себе является будущее [Int] вычисляется в другом потоке.