Reputation: 5202
What is the correct way to cache the result of future in playframework. E.g.:
val userGravatar: Future[JsValue] = RemoteGravatarService.get(user.id, user.email)
object RemoveGravatarService {
def get(userId: String, email: String): Future[JsValue] = {
Cache.getOrElse("gravatar-for-$userId", 1.hour) {
WS.url("gravatar.com/email=$email").get().asJson
}
}
}
We don't want to ask (this fictional) "Gravatar" every time, because it doesn't change that often. But we need som userGravatar info quite often locally.
Here we cache the future itself, but actually we only want to cache the result of the future. Is there a convenient/correct way to do this in play?
Upvotes: 5
Views: 1233
Reputation: 36
You could use a Promise[JsValue], something like that:
val userGravatar: Future[JsValue] = RemoteGravatarService.get(user.id, user.email)
object RemoveGravatarService {
def get(userId: String, email: String): Future[JsValue] = {
Cache.getOrElse("gravatar-for-$userId", 1.hour) {
Promise[JsValue].completeWith(WS.url("gravatar.com/email=$email").get().map(_.json)
}.future
}
}
Upvotes: 0
Reputation: 55569
There isn't a method in Play's API that will handle Future
s. You can wrap Play's cache API to handle the orElse
case where it returns a Future
. Generically:
object FutureCache {
def getOrElse[A](key: String, expiration: Int)(orElse: => Future[A])
(implicit app: Application, ct: ClassTag[A], ec: ExecutionContext): Future[A] = {
Cache.getAs[A](key).map(Future.successful).getOrElse {
val value = orElse
value onSuccess { case result => Cache.set(key, result, expiration) }
value
}
}
}
Usage:
FutureCache.getOrElse[JsValue]("gravatar-for-$userId", 3600) {
WS.url("gravatar.com/email=$email").get().map(_.json)
}
You could also create an overload for getOrElse
that uses Duration
.
Upvotes: 7