The easiest way is to quickly load the original Ajax request (without any results) and add AjaxSelfUpdatingTimerBehavior to the target component. This behavior then checks for intervals (for example, every 10 seconds or so) if there is a result. If there is a result, it should update the ans component and remove it itself.
This way you can perform the operation in a separate task without blocking Ajax calls.
To develop a bit, I created a runnable quickstart that issues 5 Ajax calls, as you described, each of which starts a random amount of time between 10 seconds and one minute. At the same time there is a responsive AjaxLink with a counter.
The basic idea is to separate the actual Ajax calls from the calls to the slow method.
add(new ListView<DataHolder>("list", list) { @Override protected void populateItem(ListItem<DataHolder> item) { DataHolder dh = item.getModelObject(); item.add(new Label("itemNumber", new PropertyModel<Integer>(dh, "number"))); Label result = new Label("itemResult", new PropertyModel<String>(dh, "result")); result.setOutputMarkupId(true); result.add(new AjaxSelfUpdatingTimerBehavior(Duration.seconds(2))); item.add(result); Thread thread = new Thread(new Processor(item.getModelObject())); thread.start(); } });
As you can see, the shortcut model no longer calls doSomeLengthyOperation() . Instead, a new thread is created that makes a heavy lift. The Processor class simply implements the Runnable interface and uses the run method to do the work (in your case, it just waits for a while in the demo).
The getter for the PropertyModel encapsulates this trick and makes it transparent, always returning quickly to prevent blocking.
public String getResult() { String retValue; if (!processed) { retValue = String.format("Still busy after %d requests", counter++); } else { retValue = result; } return retValue; }
The processed element is just a flag that the processor uses to indicate that it has made an wait (ehr working).
Since you are probably publishing more than 5 threads at a time, I would recommend using some kind of Threadpool, but this is beyond the scope of this small demo.
Disclaimer: This is not a production code. This is just for demonstration. This will not be good for your resources and will not process them gracefully. It will not work if the user clicks restart or something else happens.