Reputation: 949
The headline is probably a bit off, so here's the expanded question:
I have an user control, for example a Button
. Whenever I click the button, an expensive Runnable
should be scheduled in an ScheduledExecutorService
. Because the Runnable
runs some expensive code, I had the idea to only run the said Runnable
if the button was not pressed another time during a given time interval. If the button was pressed again within the said interval, the timer should be reset and the same Runnable
should be running after a given delay. If the button has not been pressed another time during the delay interval, the Runnable
is executed.
Is there some build-in way or can I realize this somehow?
The current implementation looks like this:
public class RepeatedCallScheduler {
private long waitForMillis;
private long systemTimeMillis;
public RepeatedCallScheduler(long waitForMillis) {
this.waitForMillis = waitForMillis;
}
public void run(Runnable runnable) {
this.systemTimeMillis = System.currentTimeMillis();
// Run logic
}
public static void main(String[] args) {
RepeatedCallScheduler scheduler = new RepeatedCallScheduler(500);
Button button = new Button();
button.setOnAction(event -> {
scheduler.run(() -> doSomething());
});
}
private static void doSomething() {
System.out.println("hello");
}
}
Example:
In this example, the time delay values 500 milliseconds, meaning 500 milliseconds after the last click on the button the method doSomething()
should run.
I click the button on time (in milliseconds) x
and the second time I click it at time x + 300
. Now the first click event should not run but at time x + 800
the scheduler should run the method doSomething()
asynchronously, as long as the button is not clicked again during x + 300
and x + 800
.
After this the program prints "hello" once, not twice.
As I asked before, is there a way to properly implement this with the use of a ScheduledExecutorService
?
Upvotes: 1
Views: 150
Reputation: 10143
Whenever you schedule some action you receive ScheduledFuture
instance which you can use to cancel
previous task and schedule new one:
private ScheduledFuture<?> task;
button.setOnAction(event -> {
if (task != null) {
// change this to true if you want to cancel already running task
task.cancel(false);
}
task = scheduler.schedule(() -> doSomething(), 500, TimeUnit.MILLISECONDS);
});
Upvotes: 1
Reputation: 26
private long waitForMillis;
private AtomicInteger taskNo;
private ScheduledExecutorService executorService;
public RepeatedCallScheduler(long waitForMillis) {
this.waitForMillis = waitForMillis;
this.taskNo = new AtomicInteger();
executorService = Executors.newScheduledThreadPool(4); // Whatever you need
}
public void run(Runnable runnable) {
int no = taskNo.incrementAndGet();
executorService.schedule(() -> {
// Check if the task should be executed
if (no == taskNo.get()) {
// Logic..
}
}, waitForMillis, TimeUnit.MILLISECONDS);
}
You could wrap the code to be executed with a container and give it an id. If the global id changed, a new task came in before execution and it should not be started.
Hope this works for you :)
Upvotes: 1