在JAVA中如何實現Debounce去抖,防止頻繁調用

在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();
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章