herman
herman

Reputation: 12305

Parallel AJAX calls in Vaadin javascript extension

I'm trying to write a simple Vaadin Extension in javascript (meaning I subclass AbstractJavaScriptExtension) that triggers a method call on the server side. This method call will e.g. load some data and update the UI of the component. Basically the idea is to have single-shot asynchronous requests for each component with the goal of having the potentially long-running database / webservice calls at the server side run in parallel.

Since I don't need to continuously update the components, using Vaadin Push would be overkill. I'd like to use plain old XHR.

So I added a javascript trigger function in my extension class like this:

addFunction("trigger", jsonArray -> command.trigger());

(this is java 8 syntax, trigger() is just a method on a Functional Interface)

And I tried simply calling this trigger immediately in my javascript connector like this:

window.com_example_AsyncTrigger = function () {
    this.trigger();
}

I wrote a dummy component that simulates slow loading using Thread.sleep() and created three instances, with 10, 6 and 2 second delays, while logging the start and end timestamps of the loading. I then extended three layouts with my extension, to add each component into when its corresponding trigger is fired.

And sure, all three triggers are fired, but they are fired within the same request (I could tell from the logging that they were executed in a single server thread). This means after about 18 seconds, all 3 components are updated at once, while I would expect the 2 second component to be shown first, then the 6 second component, and lastly the 10 second componentt, in a total time of about 10 seconds.

So, I figured out this must be because Vaadin somehow queues the function calls done when creating the connection wrappers and calls the server side methods on a single request. So I changed my javascript connector implementation to call the client side trigger() function asynchronously, like this:

window.com_example_AsyncTrigger = function () {
    var self = this;
    setTimeout(function() { self.trigger();}, 0);
}

And sure, these triggers now fire in separate requests, as I can tell from the logging that the names of the worker threads are different. The only problem is... they still run sequentially. The calls to self.trigger() are done in rapid succession, but the actual XHR requests are not done in parallel. The next one is started only after the previous one completes.

Why is this, and how can I work around it?

Upvotes: 2

Views: 869

Answers (1)

ssindelar
ssindelar

Reputation: 2843

The webserver doesn't process requests from the same client in parallel to prevent any race conditions or locking issues. To process long running requests you need to do it asynchronous to the thread that processed the request. When it is finished, write the result back to the UI, using UI.access(...). The Thread that processed the request will return and the next request will be processed. The problem is than that the result won't be send to server till the client sends the next request. But all long running requests are processed in parallel.

First option:

Poll the server regularly (there is a vaadin addon for that). Than the result will be send with the next poll.

Second option:

Instead of polling just use push. The overhead isn't that large, if you don't expect hundreds of users at the same time, it shouldn't be an issue.

Further explanation:

From what I understand about the Vaadin Framework is that it processes requests the following way:

  1. process any changes that are send from the client
  2. call your Listeners (ValueChange, Click, ...)
  3. send changes to the client

When a second thread would change the URL while the first thread is in the thrid step, this could cause serious problem because just half of the changes would be transmitted to the client. Vaadin throws an Exception when it detects that an tells you to use UI.access(...). UI.access() takes care of executing the provided Runnable directly (when no other request is processed) or asynchronously after the currently running request is finished. One finally note. The lock is session based not just UI based. So changes to different UIs in the same session (e.g. different tabs of the same application) are also processed sequentially.

Upvotes: 2

Related Questions