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.
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 thetask
method from your controller then Grails will automatically handle the calling ofdispatch()
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