在实际前端项目中, 会有一些场景 , 在用户点击按钮时 ,因手抖等各种原因多次点击 , 重复提交请求 . 因此 , 通常情况下,会要求前后端均做一些限流/防手抖策略 . 这里简单说一下各前端如何去实施的.

防抖和限流是我们再开发过程中常用的优化性能的方式

通常 , 我们会给重要请求的按钮设置限制 , 比如 500ms 只能提交一次

Android (Java)

Android 中我们使用 RxBinding 来实现 .

添加依赖

build.gradle

1
2
// 查找对应版本填入
implementation "com.jakewharton.rxbinding2:rxbinding:${version}"

RxBind.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class RxBind {

// 默认按钮防手抖时间(ms)
public static final long THROTTLE_FIRST = 500;

public static Disposable click(View view, Consumer<View> action) {
return click(view,action,THROTTLE_FIRST);
}

public static Disposable click(View view, Consumer<View> action, long delay){
return clicks(view)
.throttleFirst(delay, TimeUnit.MILLISECONDS)
.subscribe(action);
}


private static Flowable<View> clicks(@NonNull View view){
return Flowable.create(new ViewClickOnSubscribe(view), BackpressureStrategy.ERROR);
}
}

调用

1
RxBind.click(button, view -> doSomething());

这里是使用到了 RxJava 中的 throttleFirst 操作符 , 意为事件流的触发距第一次触发需要间隔500ms才能生效.

iOS (Swift)

iOS (Swift) 中我们通过 RxSwift 来实现

Podfile

1
2
3
4
# 查找对应版本填入
pod 'RxSwift', '~> ${version}'
# 看需求是否要引用此行
pod 'RxCocoa', '~> ${version}'

UIButtonExtension.swift

1
2
3
4
5
6
7
8
extension UIButton {

/// 防重复提交
var click: Observable<Void>{
return self.rx.tap
.throttle(Config.clickDelay, scheduler: MainScheduler.instance)
}
}

调用

1
2
3
4
5
6
button
.click
.subscribe(onNext: { [weak self] in
self?.doSomething()
})
.disposed(by: dispose)

前端(Javascript)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* 函数防抖 (只执行最后一次点击)
* @param fn
* @param delay
* @returns {Function}
* @constructor
*/
export const Debounce = (fn, t) => {
let delay = t || 500;
let timer;
console.log(fn)
console.log(typeof fn)
return function () {
let args = arguments;
if(timer){
clearTimeout(timer);
}
timer = setTimeout(() => {
timer = null;
fn.apply(this, args);
}, delay);
}
};


/**
* 函数节流
* @param fn
* @param interval
* @returns {Function}
* @constructor
*/
export const Throttle = (fn, t) => {
let last;
let timer;
let interval = t || 500;
return function () {
let args = arguments;
let now = +new Date();
if (last && now - last < interval) {
clearTimeout(timer);
timer = setTimeout(() => {
last = now;
//fn.apply(this, args);
}, interval);
} else {
last = now;
fn.apply(this, args);
}
}
};

使用:

1
2
3
4
5
6
7
 
methods:{
getAliyunData:Throttle(function(){
...
},1000),
}

在Vue中使用

首先定义公共js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 防抖
export function _debounce(fn, delay) {

var delay = delay || 500;
var timer;
return function () {
var th = this;
var args = arguments;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function () {
timer = null;
fn.apply(th, args);
}, delay);
};
}
// 节流
export function _throttle(fn, interval) {
var last;
var timer;
var interval = interval || 500;
return function () {
var th = this;
var args = arguments;
var now = +new Date();
if (last && now - last < interval) {
clearTimeout(timer);
timer = setTimeout(function () {
last = now;
//fn.apply(th, args);
}, interval);
} else {
last = now;
fn.apply(th, args);
}
}
}

引用

1
import { _debounce } from "@/utils/public";

调用

1
2
3
4
5
6
methods: {
// 改变场数
changefield: _debounce(function(_type, index, item) {
// do something ...
}, 500)
}

Flutter

pubspec.yaml

1
rxdart: ^0.22.2

调用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import 'package:rxdart/rxdart.dart';

final _counterSubject = BehaviorSubject<int>();

@override
void initState() {
super.initState();
_counterSubject.throttleTime(Duration(milliseconds: 500)).listen((int i) {
print(i);
});
}

RaisedButton(
onPressed: () {
_counterSubject.add(1);
},
child: Text('Test'),
)

参考链接