Scala之Future超時


最近在開發中使用akka http進行請求,返回的是一個future,並且要對future進行超時設置,不知怎么設置,因此學習了下。

一、Future阻塞

首先,scala中的future不支持內置超時,要想達到這樣的目的,可以使用Await進行阻塞,具體例子如下:

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

lazy val f = future { Thread.sleep(2000); true }
Await.result(f, 1 second)

上面的代碼將超時,報如下錯誤:

java.util.concurrent.TimeoutException:
    at scala.concurrent.impl.Promise $ DefaultPromise.ready(Promise.scala:219)
    at scala.concurrent.impl.Promise $ DefaultPromise.result(Promise.scala:223)
    at scala.concurrent.Await $$ anonfun $ result $ 1.apply(package.scala:107)
    at scala.concurrent.BlockContext $ DefaultBlockContext $ .blockOn(BlockContext.scala:53) 
...

二、非阻塞Future超時

但是,我們知道,在future上設置阻塞不是官網推薦的一種方式,因為這會浪費一個線程。因此,我們可以使用akka after實現一種非阻塞式的future超時:

import scala.concurrent._
import scala.concurrent.duration._
import ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
import akka.actor.ActorSystem
import akka.pattern.after

val system = ActorSystem("theSystem")

lazy val f = future { Thread.sleep(2000); true }
lazy val t = after(duration = 1 second, using = system.scheduler)(Future.failed(new TimeoutException("Future timed out!")))

val fWithTimeout = Future firstCompletedOf Seq(f, t)

fWithTimeout.onComplete {
case Success(x) => println(x)
case Failure(error) => println(error)
}

但是,注意了,為了確保在執行前,計時還沒有開始,必須將after設置lazy val。

但是上述這種模式的缺點是它依賴於akka,因此,我們可以使用純scala模式,來模仿實現after的功能

為了更容易使用future的超時設置,我們可以使用隱式類來擴展scala future從而支持超時:

import scala.concurrent._
import scala.concurrent.duration.FiniteDuration
import ExecutionContext.Implicits.global
import akka.actor.ActorSystem
import akka.pattern.after

implicit class FutureExtensions[T](f: Future[T]) {
def withTimeout(timeout: => Throwable)(implicit duration: FiniteDuration, system: ActorSystem): Future[T] = {
Future firstCompletedOf Seq(f, after(duration, system.scheduler)(Future.failed(timeout)))
}
}

現在,我們可以隨時很方便的給future設置超時了:

import scala.concurrent._
import scala.concurrent.duration._
import scala.util.{ Success, Failure }
import ExecutionContext.Implicits.global
import akka.actor.ActorSystem

implicit val system = ActorSystem("theSystem")
implicit val timeout = 1 second

lazy val f = future { Thread.sleep(2000); true }

f withTimeout new TimeoutException("Future timed out!") onComplete {
case Success(x) => println(x)
case Failure(error) => println(error)
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM