Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
247 views
in Technique[技术] by (71.8m points)

asynchronous - In Grails 3.2.4, controller that has an .async.task call in it sets request.asyncStarted() to true but doesn't render response

First off, this wasn't an issue in Grails 2.5.4.

I'm making an angular AJAX call to my Grails controller and the Grails controller is never responding. The front end call looks something like this..

     $http({
            method: "GET",
            url: actionLink,
            params: {}
        }).then(function successCallback(response) {

            console.log("Yaaay, I got back with some sort of response");
        }, function errorCallback(response) {
            console.log("ERROR PULLING DETAIL")
        });

I've confirmed that AJAX calls to other controllers that don't invoke any sort of .async.task return immediately, and just fine. This end point however, has two async.task's and I can tell the asyncStarted flag is being triggered after the first async.task is defined:

        log.info("ASYNC CHECK-1 - asyncSupported=" + request.asyncSupported + ", asyncStarted=" + request.isAsyncStarted())

        def availabilityTask = Item.async.task {

            JSON availability = itemService.getItemAvailabilityAsJSON(itemInstance)

            availability
        }

        log.info("ASYNC CHECK-2 - asyncSupported=" + request.asyncSupported + ", asyncStarted=" + request.isAsyncStarted())

The first request.isAsyncStarted() returns false and the second returns true. I was tipped off to this being an async issue when I turned the logging all the way up and noticed this message in my logs:

2017-02-08 18:47:54 DEBUG o.g.web.servlet.mvc.GrailsDispatcherServlet : Leaving response open for concurrent processing

Previously in Grails 2.5.4 I just had to simply call availabilityTask.get() and render the view with the appropriate model and layout specified. Now, it appears, that I have to do this in order to get the response to flush back to the front end:

   if (request.isAsyncStarted()) {
            final AsyncContext ac = request.asyncContext;
            log.info("Calling to complete async request")
            ac.dispatch()
   }

This seems annoying, since I'm not passing any promises or callables into the model and I'm already checking that the tasks are complete. Am I missing something that is forcing me to do this, or is this expected and poorly documented behavior (not called out at all anywhere on the Grails doc about Async that .dispatch() needs to be called to flush async requests..

UPDATE Feb, 10th 2017

I attempted to pass a promise into the model like:

        def availabilityTask = Item.async.task {
            JSON availability = itemService.getItemAvailabilityAsJSON(itemInstance)

            availability
        }
        render view : "myView", model:[nonAsyncItems, availabilityTask]

thinking that the result transformer would detect a Promise in the model but the auto-dispatch didn't occur. I was thinking to mimic what I saw in the Grails Async documentation but I guess the entire model has to be a PromiseMap in order for the auto-dispatch to occur? (Which isn't ideal when I have a controller pulling sync and async data)

Section from Documentation:

    import static grails.async.Promises.*

    def index() {
       render view:"myView", model: tasks( one:{ 2 * 2 },
                                           two:{ 3 * 3 } )
    }

Anyhow, it seems for now my option is to wrap controllers with any *.async.task in a larger task {} closure, or manage the dispatch manually.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

It is true that Grails previously did not start and async request for tasks. This behaviour was wrong because if the task took time then the original request could be completed and returned to the thread pool before the task has time to complete leading to unpredictable behaviour and exceptions.

This behaviour was corrected in Grails 3. If you return the Promise created by the task method from your controller then Grails will automatically handle the calling of dispatch() for you. You can see this here https://github.com/grails/grails-core/blob/3.2.x/grails-plugin-async/src/main/groovy/org/grails/plugins/web/async/mvc/AsyncActionResultTransformer.groovy#L72


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...