https://stackoverflow.com/questions/53097029/throttle-function-execution-in-dart
https://stackoverflow.com/questions/51791501/how-to-debounce-textfield-onchange-in-dart
유저가 어떤 입력을 다 끝냈을 때 어떤 연산을 실행하는 논리를 생각해보자.
완료 버튼 같은 것이 있어서 유저가 친절하게 눌러주면 연산을 수행하는 상황이라면 간단하다.
// 1. 유저의 입력
// 2. 유저가 완료 버튼 눌러줌
// 3. 연산 실행
// 4. 결과를 유저에게 보여줌
하지만 현실에서는 이런 일도 있다. 유저가 어떤 입력을 할 때 마다 실시간으로 바뀌는 결과를 출력해야 할 때도 있다.
OO 계산기, 글자 수 세기 같은 것이 좋은 예시다. 물론 이런 것들에도 위와 같은 과정을 적용해서 ‘완료 버튼’ 같은 것을 넣어줘도 괜찮겠지만, 유저는 입력 도중에 아무런 피드백을 받지 못한다.
유저와 프로그램과의 소통은 중요하다. 유저에게 ‘내가 뭔가를 제대로 하고 있구나’ 를 계속해서 알려주는 것이 중요하다.
while(유저가 입력 중) {
// 1. 유저가 무언가를 입력함
// 2. 주어진 입력에 따라서 연산
// 3. 결과를 유저에게 보여줌
}
분 당 300타의 입력을 생각했을 때 초 당 5번의 함수 호출이 있을 것이다.
2번의 연산이 매우 짧고 연산 비용이 작으면 아무런 문제가 없다.
하지만 한 번 호출하면 인공위성과 통신을 한다던지, 한 1~2초 정도 걸린다던지, 10센트씩 내야한다던지 하면 조금 생각을 해봐야 한다.
매 번 이런 연산을 해야만 할까? 실시간성이 매우 중요한 작업이 아니라면 그냥 초 당 한 번 정도만 처리 한다던지, 마지막 것만 처리한다던지 하는 것도 괜찮을 것이다.
// 타이머 초기 설정
while(유저가 입력 중) {
// 1. 유저가 무언가를 입력함
if(timer의 시간이 끝남) {
// 2. 마지막 입력을 처리
// 3. 결과를 유저에게 보여줌
// 다시 타이머 설정
}
}
// 타이머 설정
while(유저가 입력 중) {
// 1. 유저가 무언가를 입력함 -> 다시 타이머 설정
if(timer 시간이 끝남) {
// 2. 마지막 입력을 처리
// 3. 결과를 유저에게 보여줌
}
}
사용하기 쉽게 클래스로 만드는 것이 좋다.
Throttle.dart
import 'dart:async';
///
class Throttler {
// Interval timer
Timer? _interval;
// To execute next timing
Function? _nextFn;
final Duration delay;
Throttler({this.delay = const Duration(milliseconds: 1000)});
/// Call this intance as a function
void call(Function fn) {
if (_interval != null) {
_nextFn = fn;
return;
}
_interval = Timer.periodic(delay, (timer) {
if (_nextFn == null) {
cancel();
return;
}
_nextFn!();
_nextFn = null;
});
fn();
}
void cancel() {
_interval?.cancel();
_interval = null;
}
}
Debouncer.dart
import 'dart:async';
///
class Debouncer {
Timer? _timer;
final Duration delay;
Debouncer(this.delay);
/// Call this intance as a function
void call(Duration d, Function fn) {
cancelTimer();
_timer = Timer(d, () => fn());
}
void cancelTimer() {
_timer?.cancel();
_timer = null;
}
}