In the Lift project I’m currently working on, we have a long-running background task which outcomes need to be displayed on screen once computed. I would like to describe a simple enhancement for Lift bindings that allows to bind Future[T]
using CSS selectors the same way as any other simple type.
The traditional request-response lifecycle model assumes, that the client side is responsible for initiating the interaction sending HTTP request. Client’s request is processed by server that prepares and sends back HTTP response. At this point interaction is over, unless client sends another request that starts completely new request/response lifecycle.
In case of Future[T]
, this traditional model is not enough. It’s the server side that is aware when Future
evaluation is completed so, theoretically, the server side should initiate the interaction. This behaviour can be simulated using Comet. The Comet model is based on long-running HTTP request that allows server to push data to the client. When client receives data from server, new long-running HTTP request is triggered. This way, we can simulate behaviour that server side is capable of initiating the interaction and may send notifications to client at any time.
Using Comet is sometimes not desirable, though. In my case, there is a very simple Lift snippet with only some part of DOM lazy loading. This lazy-loading part is computed by a relatively long-running background task (comparing to the page load time). Once computed, information is never updated, so only one “server push” is required.
Because I didn’t want to use Comet, I had to come up with my own solution. What I wanted to achieve was the possibility to bind Future
s and LAFuture
s (Lift wrapper for Future
) using CSS selectors just like in case of simple types:
1 2 |
|
Important requirement was that the code containing Future
bindings might be rendered using IdMemoize
transform. It means that there was absolutely no guarantee, that Future
s were bound to static DOM elements visible at the first render time and never updated. Part of DOM containing nodes to which Future
s were bound could be re-rendered at any time and after each such operation, Lift should attempt to resolve these Future
s.
The code below is all you need to make it happen – the only thing required to do is to import FutureBinds._
into your snippet and you are free to bind Future
s using CSS selectors just like in the example code above.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
|
Each one second, client asks server “has Future
completed?”. If so, server tells client to update its DOM with Future
evaluation result. Otherwise, server says “Not yet. Please ask me again later”.
Alternative approach for binding Futures
has been presented by Antonio in his lift-future-canbind-example on GitHub.
It’s very clever idea that takes advantage of Lift’s <lift:lazy-load>
mechanism. The FutureBinds
code in that example looks much simpler but there is one important limitation. Under the hood, lazy load uses Lift’s comet and comets can’t be sent down to the client via AJAX at the moment. That’s just framework limitation. So even if we agree on using comet to handle Future
bindings internally, we will not be able do render code containing Future
bindings dynamically with IdMemoize
.
Sample application with my FutureBinds
implementation is available on GitHub.