当同一个方法被多次调用时取消方法调用

6
我认为我所描述的技术可能有一个名称,但我不知道它叫什么。因此,我的第一个问题是要知道这种技术的名称。
这里有一个例子:假设你正在网页上实现实时搜索。每次用户在搜索框中键入内容,你都会触发一个新的搜索查询,并尽可能频繁地更新结果。这是一件很愚蠢的事情,因为你会发送比实际需要更多的查询。每输入2-3个字母或最多每100毫秒发送一次请求可能就足够了。
因此,一种技术是安排在按下按键后尽快执行查询,并且如果仍有计划但未执行的查询,则取消它们,因为它们现在已经过时了。
更具体地说,是否有特定的模式或库可用于解决Java中的这个问题?
我必须在Swing应用程序中解决这个问题,我使用了ExecutorService,它返回ScheduledFutures,我可以取消它们。问题在于,我必须手动为我想要“缓冲”的每个方法调用创建一个Runnable,并跟踪每个Future以便取消它。
我相信我不是第一个实现这样东西的人,所以肯定有一个可重用的解决方案吧?可能在Spring中有注释和代理?

我认为你没有任何模式。流程很简单:第一次运行异步任务,只需设置标志=“BUSY”,这意味着:在第一个请求没有得到响应之前不要开始下一个搜索。 - Maxim Shoustin
2
它被称为“请求限流”。 - user177800
1
@JarrodRoberson:谢谢,这帮助我找到了一些资源。但是节流似乎与我描述的略有不同。使用节流,您会立即调度请求,如果有太多请求,则阻塞新请求(较早的请求优先)。我描述的方法则推迟初始请求,以便在出现更新请求时可以取消它们(后续请求优先)。 - ARRG
这只是语义问题;阻塞、取消、延迟,随便怎么说都行。概念就是你明确地管理请求/事务的速率,而不是让它像消防水龙一样喷射出来。 - user177800
1
@user177800 它也被称为去抖动。 - Kresten
显示剩余2条评论
3个回答

13

考虑到其他回答,并经过一些搜索,似乎确实没有符合我要求的库。

所以我自己创建了一个并放在GitHub上。未来的读者可能会对此感兴趣。

https://github.com/ThomasGirard/JDebounce

我认为它还不是非常好,但至少它可以工作并且可以被声明使用:

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

1
+1 你应该接受这个答案,因为它解决了问题,并且目前是在线上唯一的Java解决方案(或者是极少数的解决方案之一)。 - vgru

1
你需要的是所谓的防抖技术。你可以查看jQuery Throttle/Debounce插件(除了使用相同的命名空间外,与jQuery完全独立)。你需要的内容被防抖部分所涵盖:

使用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。之后由OP编写了实际的Java解决方案。


谢谢。Debouncing 是我正在寻找的术语。我点了一个赞并将接受它,除非有人能给我更多与 Java 相关的内容。 - ARRG
@ARRG:糟糕,我以为这是JavaScript(因此有这些示例)。 :) - vgru
是的,谢谢。我正在寻找的是在一个维护良好的库中支持这一点(像_.js或jQuery这样的库,但在Java世界里)。但我猜这在JavaScript中比在Java中更常见。 - ARRG
@ARRG:我偶尔在C#中使用它,比如当焦点在控件之间切换时刷新相关菜单和工具栏(例如复制/粘贴)。但是我没有Java的实现。 - vgru

1

如果不使用像执行器和 futures 这样的额外基础设施,Java 中无法解决这个问题。在 Java 中,无法以语法简洁的方式解决此问题。

您始终需要某种方法结果包装器,因为机制立即返回,但实际结果稍后检索。在您的情况下,通过 Future 完成了这一点。

您始终需要能够指定要执行的代码,以一种允许延迟执行的方式。在大多数语言中,可以使用函数指针、函数值或闭包来完成。在 Java 中,缺乏这些语言特性,通常通过传递实现某种接口的对象(例如 Runnable、Callable),以允许延迟执行代码块的方式来完成。还有其他选项,但没有一个是简单的,例如使用动态代理。

简而言之

在 Java 中无法以简洁的方式完成此操作。


看起来Java 8对lambda函数有一些支持。 - vgru
也许是因为很多企业产品还不支持Java 8,所以我还没有去了解它。 - RokL

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接