Skip to content

Instantly share code, notes, and snippets.

@Pyppe
Created August 6, 2014 13:57
Show Gist options
  • Save Pyppe/4dbce21c872db7dc28a3 to your computer and use it in GitHub Desktop.
Save Pyppe/4dbce21c872db7dc28a3 to your computer and use it in GitHub Desktop.
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success}
object FutureCache {
/**
* Returns a function which returns previously calculated Future (if one exists, and hasn't been expired)
* - If result does not exist (or has expired), we share a common `pendingFuture`
* to prevent same calculation being repeated
*/
def cached[T](cacheMillis: Long)(ft: => Future[T])(implicit ec: ExecutionContext): () => Future[T] = {
var timeToValue: Option[(Long, T)] = None
var pendingResult: Option[Future[T]] = None
val lock = new Object()
def isStillValid(time: Long) =
(System.currentTimeMillis - time) < cacheMillis
def generateAndUpdateTime() = pendingResult.getOrElse {
val future = ft
pendingResult = Some(future)
future.onComplete {
case Success(value) =>
timeToValue = Some(System.currentTimeMillis -> value)
pendingResult = None
case Failure(err) =>
pendingResult = None
}
future
}
() => lock.synchronized {
timeToValue match {
case Some((time, value)) if isStillValid(time) =>
Future.successful(value)
case _ =>
generateAndUpdateTime()
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment