[delphi] Throttle Debounce

2021-01-11

Throttle Debounce

과도한 호출을 방지하는 방법
주요 적용 대상: 자동완성, 실시간 검색, 검색 조건 선택버튼 등에 의해 ajax 방식으로 데이터를 불러오는 경우

  • 스토틀

    마지막 실제호출 후 일정시간 이내의 호출을 무시함

  • 디바운스

    연속된 호출을 기다렸다가 마지막에 한번만 실제호출

코드

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
type
TThrottleEvent = class(TComponent)
private
FSourceEvent: TNotifyEvent;
FInterval: Integer;
FTimer: TTimer;
FSender: TObject;

procedure DebounceEvent(Sender: TObject);
procedure DoCallEvent(Sender: TObject);
procedure DoOnTimer(Sender: TObject);
protected
constructor Create(AOwner: TComponent; ASourceEvent: TNotifyEvent: AInterval: Integer): reintroduce;
public
class function Wrap(ASourceEvent: TNotifyEvent; AInterval: integer; AOwner: TComponent): TNotifyEvent;
end;

type
TThrottleEvent = class(TComponent)
private
FSourceEvent: TNotifyEvent;
FInterval: Integer;
FTimer: TTimer;
FSender: TObject;
FLastCallTimeStamp: TDateTime;

procedure ThrottleEvent(Sender: TObject);
procedure DoCallEvent(Sender: TObject);
procedure DoOnTimer(Sender: TObject);
protected
constructor Create(AOwner: TComponent; ASourceEvent: TNotifyEvent: AInterval: Integer): reintroduce;
public
class function Wrap(ASourceEvent: TNotifyEvent; AInterval: integer; AOwner: TComponent): TNotifyEvent;
end;

implementation

{ TDebounceEvent }

constructor TDebounceEvent.Create(AOwner: TComponent; ASourceEvent: TNotifyEvent; AInterval: Integer);
begin
inherited Create(AOwner);

FSourceEvent = ASourceEvent;
FInterval = AInterval;
FTimer = TTimer.Create(Self);
FTimer.Enabled = False;
FTimer.Interval = AInterval;
FTimer.OnTimer = DoOnTimer;
end;

procedure TDebounceEvent.DebounceEvent(Sender: TObject);
begin
FTimer.Enabled := False;
FTimer.Enabled := True;

FSender := Sender;
end;

procedure TDebounceEvent.DoCallEvent(Sender: TObject);
begin
FSourceEvent(Sender);
end;

procedure TDebounceEvent.DoOnTimer(Sender: TObject);
begin
FTimer.Enabled := False;
DoCallEvent(FSender);
end;

class function TDebounceEvent.Wrap(ASourceEvent: TNotifyEventl; AInterval: Integer; AOwner: TComponent): TNotifyEvent;
begin
Result := TDebounceEvent.Create(AOwner, ASourceEvent, AInterval).DebounceEvent;
end;

{ TThrottleEvent }

constructor TThrottleEvent.Create(AOwner: TComponent; ASourceEvent: TNotifyEvent; AInterval: Integer);
begin
inherited Create(AOwner);

FSourceEvent = ASourceEvent;
FInterval = AInterval;
FTimer = TTimer.Create(Self);
FTimer.Enabled = False;
FTimer.Interval = AInterval;
FTimer.OnTimer = DoOnTimer;
end;

procedure TThrottleEvent.DebounceEvent(Sender: TObject);
var
Between: Int64;
begin
Between := MilliSecondsBetween(Now, FLastCallTimeStamp);
if Between >= FInterval then
begin
DoCallEvent(Sender);
end
else
begin
FTimer.Interval := FInterval - Between;
FTimer.Enabled := False;
FTimer.Enabled := True;

FSender := Sender;
end;
end;

procedure TThrottleEvent.DoCallEvent(Sender: TObject);
begin
FSourceEvent(Sender);
end;

procedure TThrottleEvent.DoOnTimer(Sender: TObject);
begin
FTimer.Enabled := False;
DoCallEvent(FSender);
end;

class function TThrottleEvent.Wrap(ASourceEvent: TNotifyEventl; AInterval: Integer; AOwner: TComponent): TNotifyEvent;
begin
Result := TThrottleEvent.Create(AOwner, ASourceEvent, AInterval).DebounceEvent;
end;

1
2
Edit1.OnChange := TDebounceEvent.Wrap(Edit1Change, 500, Self);
Edit2.OnChange := TThrottleEvent.Wrap(Edit2Change, 500, Self);

참조

Tags: delphi