2013-04-30 4 views
5

Я пытался упростить то, как я делаю фьючерсы в Scala. В какой-то момент я получил Future[Option[Future[Option[Boolean]], но я упростил его ниже. Есть ли лучший способ упростить это?Future [Option [Future [Option [Boolean]] Упрощение фьючерсов и опций?

Передача будущего «сбой» не кажется, как лучший способ сделать это. т. е. в последовательном мире, я просто вернулся «FAIL !!» в любое время, когда он потерпел неудачу, а не продолжал до конца. Есть ли другие способы?

val doSimpleWork = Future { 
    //Do any arbitrary work (can be a different function) 
    true //or false 
} 

val doComplexWork = Future { 
    //Do any arbitrary work (can be a different function) 
    Some("result") //or false 
} 

val failed = Future { 
    //Do no work at all!!! Just return 
    false 
} 

val fut1 = doSimpleWork 
val fut2 = doSimpleWork 

val fut3 = (fut1 zip fut2).map({ 
    case (true, true) => true 
    case _ => false 
}) 

val fut4 = fut3.flatMap({ 
    case true => 
    doComplexWork.flatMap({ 
     case Some("result") => 
     doSimpleWork 
     case None => 
     failed 
    }) 
    case false => 
    failed 
}) 

fut4.map({ 
    case true => 
    "SUCCESS!!!" 
    case _ => 
    "FAIL!!" 
}) 
+0

У меня создалось впечатление, что ваша логика ошибок стала чрезмерно сложной. Существует три возможных способа устранения сбоев в этом фрагменте: неудачные фьючерсы, опции, логические значения. Я бы предложил упростить эту ситуацию в качестве первого шага, возможно, превратив различные сбои в предпочтительную форму (например, только придерживаться будущих неудачных результатов). –

ответ

3

Обратите внимание, что в вашем примере, потому что вы охотно инстанцирования Futures к val, все они начинают выполняться, как только вы объявляете их (val x = Future {...}). Использование методов вместо этого сделает выполнение фьючерсов только тогда, когда они будут запрошены цепочкой выполнения.

Один из способов избежать дальнейшего вычисления будет бросать исключение, а затем обработать его с onFailure:

def one = future { println("one") ; Some(1) } 
def two = future { println("two") ; throw new Exception("no!"); 2 } 
def three = future { println("three") ; 3 } 

val f = one flatMap { 
    result1 => two flatMap { 
    result2 => three 
    } 
} 

f onFailure { 
    case e: Exception => 
    println("failed somewhere in the chain") 
} 

Здесь можно увидеть, что «три» не должен быть распечатан, так как мы fail на two. Это тот случай:

one 
two 
failed somewhere in the chain 
+0

Я не знал, что 'val x = Future {...}' фактически создавал его. Определение функции - отличный совет !!! Я также собираюсь использовать 'onFailure' или' fallbackTo', чтобы зафиксировать ошибку. Благодаря! –

1

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

def doSimpleWork = Future{ 
    //do some simple work 
    true 
    } 

    def doComplexWork = Future{ 
    //do something complex here 
    Some("result") 
    } 

    val fut1 = doSimpleWork 
    val fut2 = doSimpleWork 

    val fut = for{ 
    f1Result <- fut1 
    f2Result <- fut2 
    if (f1Result && f2Result) 
    f3Result <- doComplexWork 
    if (f3Result.isDefined) 
    f4Result <- doSimpleWork 
    } yield "success" 

    fut onComplete{ 
    case Success(value) => println("I succeeded") 
    case Failure(ex) => println("I failed: " + ex.getMessage) 
    } 

И если вы на самом деле просто хотел напечатать «успех» или «Ошибка» в конце, вы можете изменить этот последний фрагмент кода на:

fut.recover{case ex => "failed"} onSuccess{ 
    case value => println(value) 
    } 

Теперь, чтобы объяснить, что происходит. Для начала мы определили две функции, которые возвращают Futures, которые выполняют некоторую работу async. Функция doSimpleWork выполняет некоторую простую работу и возвращает индикатор boolean success/fail. Функция doComplexWork сделает что-то более сложное (и занимает много времени) и вернет параметр [String], представляющий результат. Затем мы начинаем два параллельных вызова doSimpleWork перед входом в понимание. Для for comp мы получаем результаты fut1 и fut2 (в этом порядке), прежде чем проверять, были ли они успешными. Если нет, мы остановимся здесь, и fut val будет сбой с NoSuchElementException, который вы получаете, когда такое состояние не выполняется в comp. Если бы оба были успешными, мы продолжили бы и будем вызывать функцию doComplexWork и дождаться ее результата. Затем мы проверили бы его результат, и если бы это было Some, мы отправили бы последний вызов doSimpleWork. Если это удастся, мы получим строку «success». Если вы проверите тип fut val, его тип Future[String].

Оттуда мы используем одну из функций обратного вызова асинхронного вызова, чтобы проверить, прошла ли вся последовательность вызовов либо полностью (случай Success), либо не удалось где-то в этом процессе (случай Failure), распечатать что-то связанное с тем случаем, в которое он попал. В альтернативном конечном блоке кода мы восстанавливаемся от любого возможного отказа, возвращая String «failed», а затем используйте только обратный вызов onSuccess, который будет печатать либо «успех», либо «неудачный» в зависимости от того, что произошло.

+0

Использование 'for' - хорошая идея. Я пытался «фильтровать», но «за» их встроил. Спасибо. –

3

a " Monad transformer "- это конструкция, которая позволяет объединить« эффекты »двух монад, проект« Скалаз »предоставляет несколько различных монадных трансформаторов.Мое предположение заключается в том, что вы можете использовать трансформатор Monad OptionT для упрощения кода, если вы также используете тот факт, что Option[Unit] изоморфен булевому (Some(()) == true и None == false). Вот полный пример:

import scalaz._ 
import Scalaz._ 
import scala.concurrent._ 
import ExecutionContext.Implicits.global 
import scala.concurrent.duration._ 
object Foo { 

    // We need a Monad instance for Future, here is a valid one, or you can use the implementation 
    // in the scalaz-contrib project, see http://typelevel.org 
    implicit def futureMonad(implicit executor: ExecutionContext): Monad[Future] = new Monad[Future] { 
    override def bind[A, B](fa: Future[A])(f: A ⇒ Future[B]) = fa flatMap f 
    override def point[A](a: ⇒ A) = Future(a) 
    override def map[A, B](fa: Future[A])(f: A ⇒ B) = fa map f 
    } 

    // OptionT allows you to combine the effects of the Future and Option monads 
    // to more easily work with a Future[Option[A]] 
    val doSimpleWork : OptionT[Future,Unit] = OptionT(Future { 
    // Option[Unit] is isomorphic to Boolean 
    Some(()) //or None 
    }) 

    val simpleFail : OptionT[Future,Unit] = OptionT(Future { 
    None 
    }) 

    val doComplexWork: OptionT[Future,String] = OptionT(Future { 
    Some("result") //or None 
    }) 

    val f1 = doSimpleWork 
    val f2 = doSimpleWork 
    val f3 = doComplexWork 
    val f4 = doSimpleWork 

    def main(argv: Array[String]) { 
    val result = for { 
     _ <- f1 
     // we don't get here unless both the future succeeded and the result was Some 
     _ <- f2 
     _ <- f3 
     r <- f4 
    } yield(r) 

    result.fold((_ => println("SUCCESS!!")),println("FAIL!!")) 

    // "run" will get you to the Future inside the OptionT 
    Await.result(result.run, 1 second) 
    } 
} 
+0

Это действительно здорово и интересно. благодаря –

Смежные вопросы