2016-04-15 4 views
2

Я хочу, чтобы следующий код возвращал пользовательское сообщение, когда один из методов, callfuture1() или callfuture2(), генерирует исключение. Мое понимание заключалось в том, что если какое-либо будущее не удастся, f будет неудачным будущим.невозможно обработать исключение из будущего сбоя

Однако, когда callfuture1 выдает исключение. f.onFailure не выполнен. Вместо этого я вижу, что стек вызовов остановлен в строке кода в callFuture1(), где возникло исключение, и возвращается стандартное internalError. Почему это происходит?

val f = for { 
x <- callfuture1() 
y <- callfuture2() 
} yield y 

f.onFailure { 
//send an internalserver error with some custom message 
} 

f.map { 
//send data back 
} 

==== Обновление ====

я вижу из ответов, что потенциальная проблема в том, что исключение выбрасывает за пределами будущего и, следовательно, мой код не может его поймать, что не удалось будущее , Итак, я изменил код таким образом, что Исключение происходит только в будущем. Я все еще не могу объяснить поведение, которое вижу. (Интересно, если он должен что-нибудь сделать с рамкой Play.)

def controllerfunction(id: String) = Action.async{ 

    val f = for{ 
    x <- callfuture1(id) 
    y <- callfuture2(x) 
    } yield y 

    y.onFailure{case t => 
    println("This gets printed"); 
    Ok("shit happened, but i am still ok")} 

    y.map{resp:String => Ok(resp)} 

} 

def callfuture1(id: String):Future[Obj1] = { 
    for { 
    val1 <- callfuture1.1(id) 
    val2 <- callfuture1.2(val1) 
    } yield val2 
} 

def callfuture1.2:Future[Obj3] = Future{ 
    thrown new Exception("TEST ME"); 
} 

def callfuture 1.1:Future[Obj4] = {...} 
def callfuture2: Future[String] = {....} 

Expectation. Метод callfuture1.2 бросает исключение внутри будущего, так что мое ожидание OnFailure должно быть выполнено, (который не начал выполняться), и ответ вернулся должен «Дерьмо случилось, но я до сих пор нормально»

Актуальность Структура воспроизведения возвращает InternalServerError, и я вижу столбец ошибок на моей консоли. Я вижу, что printlin («Это печатается») выполняется.

Не могу понять, что происходит. Какие-нибудь идеи?

==== Обновление 2 =====

Я проверил, что проблема возникает только при вызове внутри контроллера рамки игры (я использую играть 2.5). Как автономная программа scala, все работает так, как ожидалось. Я считаю, что обработка ошибок воспроизведения захватывает исключительное исключение и печатает трассировку стека. Я думаю, что это должно происходить только в среде разработки.

+1

Попробуйте 'val f1 = callfuture1(); val f2 = callfuture2; f = для {x <- f1; y <- f2} дают y'. Разница в том, что в вашем случае фьючерсы выполняются последовательно (y ждет завершения вызова future1). – chuwy

+0

Что вы подразумеваете под «stack stop»? Что такое «стандартная внутренняя ошибка»? Откуда вы знаете, что 'onFailure' не выполняется? Какова фактическая ошибка? – Dima

+1

Также. обратите внимание, что 'onFailure' может использоваться только для побочных эффектов. Он не может изменить исключение, содержащееся в «Будущем». Если вы хотите возиться с исключением, вам нужно использовать 'transform' или' recover' – Dima

ответ

2

Это может произойти, если callfuture1 выбрасывает «за пределы будущего». Ваша для понимания является обессахаренной в этом:

val f = callfuture1.flatMap{ x => 
    callfuture2.map{ y => 
    y 
    } 
} 

Если callfuture2 выбрасывает сразу (в отличие от возвращения отказавшего будущего), вы все равно в конечном итоге с отказавшим будущем, потому что callfuture2 называется внутри Future.flatMap, который ловит исключения и превращает их в неудачные фьючерсы (то же самое для Future.map).

Ситуация изменилась для callfuture1: если она сразу же выбрасывает, там нет Future.map или Future.flatMap, чтобы превратить ее в неудавшееся будущее.

В общем, вы должны стараться избегать использования метода, который возвращает Future, а также может выдать ошибку. Это означает, что если callfuture1 делает все, что может бросить, оно должно поймать это и превратить исключение в неудавшееся будущее, которое вы затем вернете.

UPDATE: Что касается вашего обновления о том, как вы ожидали «Дерьмо случилось, но я все еще в порядке» должны быть возвращены:

Как уже намекали Дима в комментарии, Future.onFailure могут быть использованы только для побочных эффектов , Фьючерсы неизменны. Если вы хотите восстановить неудавшееся исключение, нет способа изменить исходное (неудачное) будущее, и все, что вы действительно можете сделать, это преобразовать его в новое будущее. Посмотрите на Future.recover. Он делает именно то, что вам нужно, а именно позволяет преобразовать входное будущее, сопоставляя неудавшийся результат (если есть) и превращая его в успешное будущее. Это эквивалент предложения catch, но для фьючерсов. Конкретно то, что вы действительно хотели сделать, это примерно так:

def controllerfunction(id: String) = Action.async{ 
    val f = for{ 
    x <- callfuture1(id) 
    y <- callfuture2(x) 
    } yield y 

    f.map{ resp: String => 
    Ok(resp) 
    }.recover{ 
    case t: Throwable => 
     println("This gets printed"); 
     Ok("shit happened, but i am still ok") 
    } 
} 
+0

Я обновил вопрос , я обратился к проблеме исключения, выброшенного за пределы будущего, но я до сих пор вижу поведение, которое я не могу объяснить :(какие-нибудь идеи? – konquestor

+0

См. мое обновление –

0

Кажется, что внутри callfuture1() вы не оборачивать весь ваш процесс внутри конструктора будущего как

def callfuture1(): Future[?] = Future { 
    val x = ... 
    x 
} 

но ваш код, кажется,

def callfuture1(): Future[?] = { 
    val x = ... // some error happen here 
    Future(x) 
} 

так, потому что это за пределами будущего, ваш ошибка бросается прямо в ваш программный код

+0

ответы не могут отпускать вопросы, использовать комментарии –

+0

Я знаю, но у меня пока нет достаточной репутации – bthuillier

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