Skip to content

Instantly share code, notes, and snippets.

@Pyppe
Created May 21, 2014 15:29
Show Gist options
  • Save Pyppe/52dc56f8d74a4a2eff9c to your computer and use it in GitHub Desktop.
Save Pyppe/52dc56f8d74a4a2eff9c to your computer and use it in GitHub Desktop.
CachedFuture.scala
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success}
object CachedFuture {
/**
* Returns a function which returns previously calculated Future (if one exists, and hasn't expired)
* - If result does not exist (or has expired), we share a common `pendingResult`
* to prevent same calculation being repeated.
*/
def apply[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()
}
}
}
}
@Pyppe
Copy link
Author

Pyppe commented May 21, 2014

Use e.g. like this:

// cachedFunction
val cachedWork: () => Future[HeavyComputation] = CachedFuture(20*60*2000) {
  futureOfSomeHeavyComputation()
}

// usage
cachedWork()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment