Cancelling method calls when the same method is called multiple time

3.8k views Asked by At

I think there's probably a name for what I'm describing here, but I don't know it. So my first question would be to know the name of this technique.

Here's an example: suppose you're implementing live search on a web page. Everytime the user types in the search box, you fire a new search query, and the results are updated as often as possible. This is a stupid thing to do because you'll send much more queries than you actually need. Sending a request once per 2-3 letters or at most once per 100 ms is probably sufficient.

A technique is thus to schedule the queries to be executed soon after a key is typed, and if there are still queries that were planned but not executed, cancel them since they're obsolete now.


Now more specifically, are there specific patterns or librairies for solving this problem in Java ?

I had to solve the problem in a Swing app, and I used an ExecutorService, which returned ScheduledFutures that I could cancel. The problem is that I had to manually create a Runnable for each method call I wanted to "buffer", and keep track of each Future to cancel it.

I'm sure I'm not the first person to implement something like this, so there must be a reusable solution somewhere ? Possibly something in Spring with annotations and proxies ?

3

There are 3 answers

1
ARRG On BEST ANSWER

Given the other answers, and after some searching, it seems there's indeed no library that does what I wanted.

I created one and put it on GitHub. Future readers of this question may find it interesting.

https://github.com/ThomasGirard/JDebounce

I don't think it's very good yet but at least it works and can be used declaratively:

@Debounce(delayMilliseconds = 100)
public void debouncedMethod(int callID, DebounceTest callback) { }
4
vgru On

What you need is called debouncing. You should check the jQuery Throttle/Debounce plugin (which is btw totally independent of jQuery except for using the same namespace). What you need is covered by the debounce part:

Using jQuery throttle / debounce, you can pass a delay and function to $.debounce to get a new function, that when called repetitively, executes the original function just once per “bunch” of calls, effectively coalescing multiple sequential calls into a single execution at either the beginning or end.

Underscore.js has the same method:

_.debounce(function, wait, [immediate]) 

Creates and returns a new debounced version of the passed function which will postpone its execution until after wait milliseconds have elapsed since the last time it was invoked. Useful for implementing behavior that should only happen after the input has stopped arriving. For example: rendering a preview of a Markdown comment, recalculating a layout after the window has stopped being resized, and so on.

// example: debounce layout calculation on window resize
var lazyLayout = _.debounce(calculateLayout, 300);
$(window).resize(lazyLayout);

[Edit]

I mistakenly read "Javascript" instead of Java. Actual Java solution was written by OP afterwards.

2
RokL On

This is not solvable in Java without using some extra infrastructure like you did with executor and futures. It is not possible to solve this in syntactically concise manner in Java.

You will always need some sort of method result wrapper, because the mechanism returns immediately but the actual result is retrieved later. In your case this was accomplished via Future.

You will always need to be able to specify code to be executed in a manner that will allow delayed execution. In most languages this is accomplished using function pointers or function values or closures. In Java, lacking these language features, this is usually accomplished by passing an object that implements some sort of interface such as Runnable, Callable, that allows delayed execution of a block of code. There are other options but none of them are simple, such as using a dynamic proxy.

tl;dr

Can't do this in concise manner in Java.