Using promises within a Ratpack Handlebars helper

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

Using promises within a Ratpack Handlebars helper

Antony Jones
Hi,

I've encountered an issue trying to call one of my services (which is entirely async) from a handlebars helper, in order to render some values to the page.

Fundamentally the issue seems to be that Handlebars helpers make use of 'return' to render values to the page, so the method returns before the promise is resolved.

My helper's apply method looks something like this (obviously using a dummy promise for terseness):

    @Override
    CharSequence apply(String location, Options options) throws IOException {

           Promise.value('some resolved value').then { String nav ->
              return nav
          }

    }

Obviously what actually happens here is that the promise itself is returned from the helper, which is not the intention.

I'm hesitant to create a blocking observable for this - I would expect the handlebars helper to use something like the 'render' method a-la ratpack handlers.

Cheers,
Antony
Reply | Threaded
Open this post in threaded view
|

Re: Using promises within a Ratpack Handlebars helper

Antony Jones
So after a few discussions with Marcin Erdmann and Gus Power, I've concluded that the Handlebars integration isn't deep enough to handle async operations.

My resolution to this therefore, is to block.

The solution for this is below:

    @Override
    CharSequence apply(String location, Options options) throws IOException {

        Map<String, ?> model = Promises.await(Promise.value([hello:'there'])

        return new Handlebars.SafeString(model.toString())

    }

    static <T> T await(Promise<T> promise) {
        CompletableFuture<T> future = new CompletableFuture<>()

        Execution.fork().start {
            promise.then { value ->
                future.complete(value)
            }
        }

        return future.get()
    }

Testing this is a matter of wrapping the apply method of the helper in an ExecHarness:

def 'my test method'() {

           when:
               ExecHarness.yieldSingle {
                     apply([:], location)
               }

}

Certainly not optimal, but it seems to work.

Credit to Tom Akehurst for the Promises.await functionality.
Reply | Threaded
Open this post in threaded view
|

Re: Using promises within a Ratpack Handlebars helper

Marcin Erdmann
Antony,

Just be aware that you are blocking on a compute thread there. This will degrade performance of your application.
Reply | Threaded
Open this post in threaded view
|

Re: Using promises within a Ratpack Handlebars helper

Luke Daley
Administrator
What Marcin said.

I'll look into this today. We'll need to find a way to have the helper run on a blocking thread. Absolute worst case is that the whole render happens on a blocking thread. In that case you can use Blocking.on().
Reply | Threaded
Open this post in threaded view
|

Re: Using promises within a Ratpack Handlebars helper

Antony Jones
Thanks Marcin and Luke.

I look forward to your findings!

It's definitely affecting performance - to a much greater extent than we imagined. I'm going to try a workaround until there is a better solution to blocking within the helper.

Cheers,
Antony
Reply | Threaded
Open this post in threaded view
|

Re: Using promises within a Ratpack Handlebars helper

Luke Daley
Administrator

I couldn’t find any way to support async execution of helpers either. This means that you need to compile and/or “apply” your templates on a blocking thread. Then you can use `Blocking.on()`. I say “and/or” here because I’m not sure whether helpers get executed on compile or apply… probably the latter.

To do this, you are going to have to fork the renderer, and effectively execute this try block in a Blocking.get(): https://github.com/ratpack/ratpack/blob/master/ratpack-handlebars/src/main/java/ratpack/handlebars/internal/HandlebarsTemplateRenderer.java#L44-44

You’ll just need to bind your impl with Guice after the HandlebarsModule.

Longer term, we could add a boolean flag to the https://github.com/ratpack/ratpack/blob/master/ratpack-handlebars/src/main/java/ratpack/handlebars/Template.java to indicate whether or not the template is going to need to block and therefore to compile/apply it on a blocking thread.
Reply | Threaded
Open this post in threaded view
|

Re: Using promises within a Ratpack Handlebars helper

Kevin Richards
Is there any updates to this?  We wanted to do exactly the same thing.  Luke, is this a future feature?

Cheers

Kevin