在JAVA中如何实现Debounce去抖,防止频繁调用

作者: jekkay 分类: java,默认 发布时间: 2019-04-23 11:52

在JAVA中如何实现Debounce去抖,防止频繁调用


1. 概述

在JS中有很好的Debounce库,可以用来去抖,防止一个接口在短时间内频繁调用,可以抑制住相关的调用。

最近翻阅了下JAVA相关资料,但是没找到一个简单的可以用于去抖的类。为此,我专门封装了一个简单的去抖实用类,可以轻松地实现Debounce相关的功能SimpleDebounceCallable类。

http://www.easysb.cn/2019/04/310.html

2. SimpleDebounceCallable使用

为解决此问题,我专门封装了SimpleDebounceCallable类,具体可以参考第三节实现部门。其使用方法有两种,下面分别介绍下。

2.1 自动debounce抑制

自动监测是否处于抑制状态,如果不是就会调用函数。

// 定义成员表变量或者静态变量, 设置抑制时间 5秒
private final SimpleDebounceCallable<Integer> debounce = new SimpleDebounceCallable<>(1000 * 5);
.....

String key = "hello-call"
Optional<Integer> ret = debounce.debounce(() -> {
  int c = 0;
  (处理代码)....

  return c;
}, key);

如果只提供给唯一的地方调用,那么我就不需要用key来区分,可以直接用默认的key即可,如下:

// 定义成员表变量或者静态变量, 设置抑制时间 5秒
private final SimpleDebounceCallable<Integer> debounce = new SimpleDebounceCallable<>(1000 * 5);
.....

// 此时不需要key,直接用默认的即可
Optional<Integer> ret = debounce.debounce(() -> {
  int c = 0;
  (处理代码)....

  return c;
});

2.2 主动debounce抑制

主动监测是否处于抑制状态,然后觉得是否去调用函数。

// 定义成员表变量或者静态变量, 设置抑制时间 5秒
private final SimpleDebounceCallable<Integer> debounce = new SimpleDebounceCallable<>(1000 * 5); 

...

String key = "hello-call"
if (debounce.tick(key) > 0) {
    Optional<Integer> ret = debounce.debounce(() -> {
      int c = 0;
      (处理代码)....

      return c;
    });
}

如果只提供给唯一的地方调用,那么我就不需要用key来区分,可以直接用默认的key即可,如下:

// 定义成员表变量或者静态变量, 设置抑制时间 5秒
private final SimpleDebounceCallable<Integer> debounce = new SimpleDebounceCallable<>(1000 * 5);
...

// 此时不需要key,直接用默认即可
if (debounce.tick() > 0) {
    Optional<Integer> ret = debounce.debounce(() -> {
      int c = 0;
      (处理代码)....

      return c;
    });
}

3. SimpleDebounceCallable实现

接口Ticking的定义如下:

// http://www.easysb.cn/2019/04/310.html

public interface Ticking {
    int tick();
}

SimpleDebounceCallable的具体实现如下:

// http://www.easysb.cn/2019/04/310.html

@Slf4j
public class SimpleDebounceCallable<T> implements Ticking {
    final private static String DEFAULT_KEY = "__default";
    final private static String REMOVE_TIMEOUT_KEY = "__clear_time_out";
    // default timeout is 10 minutes
    final private static long DEFAULT_TIMEOUT = DateTimeUtils.MS_PER_MINUTE * 10;

    final private Map<String, Long> lastTickTimeMap = Maps.newConcurrentMap();

    @Setter
    @Getter
    private long timeout = 0;


    public SimpleDebounceCallable() {
        this(DEFAULT_TIMEOUT);
    }

    public SimpleDebounceCallable(long timeout) {
        this.timeout = timeout;
    }

    @Override
    public int tick() {
        return replaceIfTimeout(DEFAULT_KEY) ? 1 : 0;
    }

    public int tick(String key) {
        return replaceIfTimeout(key) ? 1 : 0;
    }

    private boolean replaceIfTimeout(String key) {
        boolean replace = false;
        synchronized (lastTickTimeMap) {
            if (isExpiredValue(lastTickTimeMap.get(key))) {
                lastTickTimeMap.put(key, System.currentTimeMillis());
                replace = true;
            }
        }
        return replace;
    }

    private boolean isExpiredValue(Long val) {
        return val == null || Math.abs(System.currentTimeMillis() - val.longValue()) >= timeout;
    }

    private void removeTimeoutItems() {
        if (MapUtils.isEmpty(lastTickTimeMap)) {
            return;
        }
        synchronized (lastTickTimeMap) {
            Iterator<Map.Entry<String, Long>> iterator = lastTickTimeMap.entrySet().iterator();
            while (iterator.hasNext()) {
                if (isExpiredValue(iterator.next().getValue())) {
                    iterator.remove();
                }
            }
        }
    }

    /**
     * debounce to execute specified lambda function
     *
     * @param callable lambda function to be call if available
     * @return Optional<T>
     */
    public Optional<T> debounce(Callable<T> callable) {
        try {
            if (replaceIfTimeout(REMOVE_TIMEOUT_KEY)) {
                removeTimeoutItems();
            }
            return Optional.ofNullable(callable.call());
        } catch (Exception e) {
            log.error("debounce", e);
        }
        return Optional.empty();
    }

    public Optional<T> debounce(Callable<T> callable, String key) {
        return replaceIfTimeout(key) ? debounce(callable) : Optional.empty();
    }
}

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

电子邮件地址不会被公开。

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据