Отмена вызовов метода, когда один и тот же метод вызывается несколько раз

Я думаю, что, вероятно, есть название для того, что я здесь описываю, но я его не знаю. Итак, мой первый вопрос — узнать название этой техники.

Вот пример: предположим, вы реализуете поиск в реальном времени на веб-странице. Каждый раз, когда пользователь вводит текст в поле поиска, вы запускаете новый поисковый запрос, и результаты обновляются как можно чаще. Это глупо, потому что вы отправите гораздо больше запросов, чем вам действительно нужно. Отправка запроса один раз в 2-3 письма или максимум один раз в 100 мс, вероятно, достаточна.

Таким образом, метод заключается в том, чтобы запланировать выполнение запросов вскоре после ввода ключа, и если все еще есть запланированные, но не выполненные запросы, отменить их, поскольку они устарели.


Теперь, более конкретно, существуют ли конкретные шаблоны или библиотеки для решения этой проблемы в Java?

Мне пришлось решить проблему в приложении Swing, и я использовал ExecutorService, который возвращал ScheduledFuture, которые я мог отменить. Проблема в том, что мне приходилось вручную создавать Runnable для каждого вызова метода, который я хотел «буферизировать», и отслеживать каждое будущее, чтобы отменить его.

Я уверен, что я не первый, кто реализовал что-то подобное, поэтому где-то должно быть многоразовое решение? Возможно что-то в Spring с аннотациями и прокси?


person ARRG    schedule 10.09.2013    source источник
comment
Я не думаю, что у вас есть какая-то закономерность. Процесс прост: вы запускаете асинхронную задачу в первый раз и просто устанавливаете флаг = BUSY, что означает: не начинать следующий поиск, пока не будет получен ответ на первый запрос.   -  person Maxim Shoustin    schedule 10.09.2013
comment
это называется *регулирование запросов   -  person    schedule 10.09.2013
comment
@JarrodRoberson: спасибо, это помогло мне найти некоторые ресурсы. Но дросселирование, похоже, немного отличается от того, что я описал. При регулировании вы отправляете запросы немедленно, а затем блокируете новые, если их слишком много (более ранние запросы имеют приоритет). Описанный мной подход задерживает первоначальные запросы, чтобы их можно было отменить в случае поступления новых (более поздние запросы имеют приоритет).   -  person ARRG    schedule 10.09.2013
comment
это просто семантика; блокировать, отменять, задерживать, что угодно. Идея заключается в том, что вы явно управляете скоростью запросов/транзакций, а не просто позволяете этому быть пожарным шлангом.   -  person    schedule 10.09.2013
comment
Я думаю, что слово, которое вы ищете, это темп. См. также Алгоритм Нэгла о том, как он был реализован в TCP для решения очень похожей проблемы с telnet 30 лет назад: en.wikipedia.org/wiki/Nagle's_algorithm   -  person John Deters    schedule 11.09.2013
comment
@user177800 user177800 Это также называется устранением дребезга.   -  person kres0345    schedule 04.06.2019


Ответы (3)


Учитывая другие ответы и после некоторых поисков, кажется, что действительно нет библиотеки, которая делала бы то, что я хотел.

Я создал один и разместил его на GitHub. Будущие читатели этого вопроса могут найти его интересным.

https://github.com/ThomasGirard/JDebounce

Я не думаю, что это еще очень хорошо, но, по крайней мере, это работает и может использоваться декларативно:

@Debounce(delayMilliseconds = 100)
public void debouncedMethod(int callID, DebounceTest callback) { }
person ARRG    schedule 12.09.2013
comment
+1 Вместо этого вы должны принять этот ответ, он решает проблему и в настоящее время является единственным решением Java (или одним из очень немногих решений), доступным в Интернете. - person Groo; 12.09.2013

Это не решаемо в Java без использования дополнительной инфраструктуры, как вы сделали с исполнителем и фьючерсами. В Java невозможно решить это синтаксически кратким образом.

Вам всегда будет нужна какая-то оболочка результата метода, потому что механизм возвращается немедленно, но фактический результат извлекается позже. В вашем случае это было достигнуто через Future.

Вам всегда нужно будет иметь возможность указать код для выполнения таким образом, чтобы разрешить отложенное выполнение. В большинстве языков это достигается с помощью указателей функций, значений функций или замыканий. В Java, где отсутствуют эти языковые функции, это обычно достигается путем передачи объекта, реализующего какой-либо интерфейс, такой как Runnable, Callable, который позволяет выполнять отложенное выполнение блока кода. Есть и другие варианты, но ни один из них не является простым, например, использование динамического прокси.

tl;dr

Невозможно сделать это лаконично на Java.

person RokL    schedule 11.09.2013
comment
Похоже, в Java 8 есть некоторая поддержка лямбда-функций. - person Groo; 11.09.2013
comment
Возможно, я еще не смотрел на Java 8, потому что многие корпоративные продукты еще не поддерживают ее. - person RokL; 11.09.2013

То, что вам нужно, называется debouncing. Вы должны проверить плагин jQuery Throttle/Debounce (который, кстати, полностью независим jQuery, за исключением использования того же пространства имен). То, что вам нужно, описано в разделе debounce:

Используя jQuery Throttle/Debounce, вы можете передать задержку и функцию в $.debounce, чтобы получить новую функцию, которая при повторном вызове выполняет исходную функцию только один раз для «группы» вызовов, эффективно объединяя несколько последовательных вызовов. вызовы в одно выполнение либо в начале, либо в конце.

Underscore.js использует тот же метод:

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

Создает и возвращает новую отклоненную версию переданной функции, выполнение которой будет отложено до тех пор, пока не пройдет wait миллисекунд с момента последнего вызова. Полезно для реализации поведения, которое должно произойти только после того, как входные данные перестанут поступать. Например: предварительный просмотр комментария Markdown, пересчет макета после того, как размер окна перестал изменяться, и так далее.

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

[Редактировать]

Я по ошибке прочитал «Javascript» вместо Java. Фактическое решение Java было написано OP впоследствии.

person Groo    schedule 11.09.2013
comment
Спасибо. Разоблачение — это термин, который я искал. Я добавил +1 к вашему ответу и приму его, если кто-то не сможет дать мне что-то более специфичное для Java. - person ARRG; 11.09.2013
comment
@ARRG: о, дерьмо, я думал, что это Javascript (отсюда и эти примеры). :) - person Groo; 11.09.2013
comment
Ага, спасибо. Я искал поддержку этого в хорошо поддерживаемой библиотеке (например, _.js или jQuery, но в мире java). Но я предполагаю, что это то, что чаще требуется в javascript, чем в Java. - person ARRG; 11.09.2013
comment
@ARRG: время от времени я использую его в C#, например, при обновлении связанных меню и панелей инструментов (например, копирование/вставка) при смещении фокуса между элементами управления. Однако у вас нет реализации Java. - person Groo; 11.09.2013