debounce : 이벤트가 발생 한 후 인터벌 n 초 동안 아무 다른 이벤트가 일어나지 않아야 그 이벤트가 실행된다. 만약 n초 사이에 다른 이벤트가 발생하면 그 때부터 다시 n초가 리셋되서 카운팅된다.
만약 인터벌이 5초이면) 이벤트 실행 후 5초 간 아무 이벤트가 일어나지 않아야 그 이벤트가 실행된다.
만약 5초 전에 다른 이벤트가 발생하면, 그 때부터 다시 5초가 카운팅된다.
A ---3sec---> B ----2sec---> C ---5sec---> D ---2sec---> E---5sec--->
여기서는, c와 e만 발생 후 5초간 아무 이벤트가 없었기 때문에 이벤트가 발생한다.
throttleFirst : 만약 인터벌이 5초이면) 5초간 실행된 이벤트 중 가장 첫번째 이벤트가 실행된다. debounce와 다른 점은, a 가 발생되고 3초 후 b 가 발생됐다고 해서 새로 5초가 카운팅 되는 것이 아님.
A---3sec---> B---3sec--->C
debounce 라면 B에서 다시 5초가 리셋 되었겠지만, throttleFirst에서는, B와 C 사이의 5초에서, 그 중 가장 첫번째 발생한 이벤트인 A가 실행된다.
throttleLast : 만약 인터벌이 5초이면) 5초간 실행된 이벤트 중 가장 마지막 이벤트가 실행된다.
A ---3sec---> B---3sec--->C
throttleLast에서는, B와 C 사이의 5초에서, 그 중 가장 마지막에 발생한 이벤트인 B가 실행된다.
debounce 와 throttle 둘다 search / 버튼 클릭 후 api를 요청 할 때 적절히 사용하면 api 요청 남용을 막을 수 있다.
fun <T> throttleFirst(
skipMs: Long = 300L,
coroutineScope: CoroutineScope,
destinationFunction: (T) -> Unit
): (T) -> Unit {
var throttleJob: Job? = null
return { param: T ->
if (throttleJob?.isCompleted != false) {
throttleJob = coroutineScope.launch {
destinationFunction(param)
delay(skipMs)
}
}
}
}
fun <T> throttleLatest(
intervalMs: Long = 300L,
coroutineScope: CoroutineScope,
destinationFunction: (T) -> Unit
): (T) -> Unit {
var throttleJob: Job? = null
var latestParam: T
return { param: T ->
latestParam = param
if (throttleJob?.isCompleted != false) {
throttleJob = coroutineScope.launch {
delay(intervalMs)
latestParam.let(destinationFunction)
}
}
}
}
fun <T> debounce(
waitMs: Long = 300L,
coroutineScope: CoroutineScope,
destinationFunction: (T) -> Unit
): (T) -> Unit {
var debounceJob: Job? = null
return { param: T ->
debounceJob?.cancel()
debounceJob = coroutineScope.launch {
delay(waitMs)
destinationFunction(param)
}
}
}