2016-09-14 8 views
1

スカラ先物: With Await.resultプログラムを終了する前に、未来が完了するのを待つことができます。、Future.onFailureが完了するのを待つ方法は?

プログラムを終了する前にFuture.onFailureが完了するのを待つ方法はありますか?

package test 

import scala.concurrent.duration.Duration 
import scala.concurrent.{Await, Future} 
import scala.concurrent.ExecutionContext.Implicits.global 

object Test5 extends App { 
    def step(i: Int): Future[String] = Future { 
    Thread.sleep(1000) 
    if (i == 3) throw new Exception("exception on t3") 
    Thread.sleep(1000); val s = s"done: $i"; println(s); s 
    } 

    val task: Future[String] = step(1).flatMap(_ => step(2).flatMap(_ => step(3))) 

    task.onFailure { 
    case e: Throwable => println("task.onFailure. START: " + e); Thread.sleep(3000); println("task.onFailure. END.") 
    } 

    try { 
    val result: String = Await.result(task, Duration.Inf) 
    println("RESULT: " + result) 
    } catch { 
    case e: Throwable => println("Await.result.try catch: " + e) 
    } 

    // without this, "task.onFailure. END." does not get printed. 
    // How can I replace this line with something such as Await.result([the task.onFailure I set previously]) 
    Thread.sleep(5000) 

    println("END") 
} 

注:代わりにtask.onFailureを使用しての、私は(例のように)Await.result上の例外をキャッチすることができます。しかし、task.onFailureを使用することをお勧めします。


更新 溶液をライアンによって提案されているようtransform又はrecoverを使用することです。私の場合、何かが必要でしたが、私はこの代替の答えを追加しました:on scala, how to wait for a Future.onFailure to complete?

答えて

1

あなたがしたいすべてがあなたは私がお勧めしたい実行されたことを知っているような副作用を配列決定することである場合'andThen'を使用します。

+0

はい、それはもっと簡単です。ありがとう。 –

1

onFailureUnitを返します。これをブロックする方法はありません。

Await.resultで例外をキャッチするか、または例外として副作用としてtransformまたはrecoverを使用してください。

+0

ありがとう、 'トランスフォーム'と 'リカバリ'は私にこの代替案を構築する方法を理解する助けになりました:http://stackoverflow.com/questions/39495546/on-scala-how-to-wait-for-a-future-onfailure -to-complete/39508675#39508675 –

0

できません。 Future#onFailureUnitを返すので、実行をブロックするためのハンドルはありません。

とにかくtaskの完了時にを遮断しているので、あなたのコード内catch句を使用すると、onFailureからになるだろう何に相当します。つまり、Futureが失敗した場合、catchの本体は、メインスレッド上で実行されているため(onCompleteではない)、プログラムが終了する前に実行されます。

+0

将来的に例外を伴って失敗すると 'onFailure'が呼び出されます。あなたは 'onFailure'の未来を待つことができます。 – pamu

+1

@pamu元の未来を待っているだけでは不十分です。 'onFailure'コールバックは非同期にスケジュールされます。つまり、元の未来が完了してから任意の時間が発生することがあります。 Viktorが言っているように、 –

0

次のように我々はonComplete2onCompleteWith2Futureを豊かにすることができます

import scala.concurrent.{ExecutionContext, Future, Promise} 
import scala.util.Try 

object FutureUtils { 
    implicit class RichFuture[T](val self: Future[T]) { 
    def onComplete2[U](f: (Try[T]) => U)(implicit executor: ExecutionContext): Future[T] = { 
     val p = Promise[T]() 
     self.onComplete { r: Try[T] => f(r); p.complete(r) } 
     p.future 
    } 

    def onCompleteWith2[U](f: (Try[T]) => Future[U])(implicit executor: ExecutionContext): Future[T] = { 
     val p = Promise[T]() 
     self.onComplete { r: Try[T] => f(r).onComplete(_ => p.complete(r)) } 
     p.future 
    }  
    } 
} 

は、次のように私は場合それを使用する:次のように

import scala.concurrent.duration.Duration 
import scala.concurrent.{Await, Future} 
import scala.concurrent.ExecutionContext.Implicits.global 
import FutureUtils._ 

import scala.util.{Failure, Success, Try} 

object Test5 extends App { 
    def stepf(i: Int): Future[String] = Future { step(i) } 

    def step(i: Int): String = { 
    Thread.sleep(1000) 
    if (i == 3) throw new Exception("exception on t3") 
    Thread.sleep(1000); val s = s"done: $i"; println(s); s 
    } 

    val task1: Future[String] = 
    stepf(1).flatMap(_ => stepf(2).flatMap(_ => stepf(3))) 

    // the result of step(10) and step(11) is ignored 
    val task2: Future[String] = task1.onComplete2((r: Try[String]) => r match { 
    case Success(s) => step(10) 
    case Failure(e) => step(11) 
    }) 

/* 
    // If I want to recover (and so, to avoid an exception on Await.result: 
    val task3 = task2.recover {case e: Throwable => step(12) } 


    // I can use onCompleteWith2 instead of onComplete2 
    val task2: Future[String] = task1.onCompleteWith2((r: Try[String]) => r match { 
    case Success(s) => stepf(10) 
    case Failure(e) => stepf(11) 
    }) 
*/ 

    try { 
    val result = Await.result(task2, Duration.Inf) 
    println("RESULT: " + result) 
    } catch { 
    // see my comment above to remove this 
    case e: Throwable => println("Await.result.try catch: " + e) 
    } 

    println("END.") 
} 

を実行は次のとおりです。

done: 1 
done: 2 
done: 11 
Await.result.try catch: java.lang.Exception: exception on t3 
END. 

例外をstep(3)にスローしないと実行は次のようになります。その結果が「完了:3」されることに注意してください、「完了:10」:ない

done: 1 
done: 2 
done: 3 
done: 10 
RESULT: done: 3 
END. 
+0

は 'future.andThen'で十分です。この 'onComplete2'と似ています(' PartialFunction'を使います)。 'onCompleteWith2'は' Future'を連鎖させるのにまだ役立つかもしれません。 –

関連する問題