diff --git a/js/debounce/demos/debounced-search/index.html b/js/debounce/demos/debounced-search/index.html
index fbf8b7dc74..f7a68c4b7c 100644
--- a/js/debounce/demos/debounced-search/index.html
+++ b/js/debounce/demos/debounced-search/index.html
@@ -163,17 +163,12 @@
const searchInput = searchForm.querySelector('[type="search"]')
const searchResults = document.querySelector('.search-results')
- function debounce(callee, timeoutMs) {
+ function debounce(callback, delay) {
+ let timer;
return function perform(...args) {
- let previousCall = this.lastCall
- this.lastCall = Date.now()
-
- if (previousCall && ((this.lastCall - previousCall) <= timeoutMs)) {
- clearTimeout(this.lastCallTimer)
- }
-
- this.lastCallTimer = setTimeout(() => callee(...args), timeoutMs)
- }
+ clearTimeout(timer);
+ timer = setTimeout(() => callback.apply(this, args), delay);
+ };
}
function handleInput(e) {
diff --git a/js/debounce/index.md b/js/debounce/index.md
index b30d08de87..2c85f43121 100644
--- a/js/debounce/index.md
+++ b/js/debounce/index.md
@@ -233,25 +233,32 @@ const server = {
Итак, `debounce()` — это функция высшего порядка, принимающая аргументом функцию, которую надо «отложить».
-Поехали. Аргументами будут функция, которую надо «откладывать», и интервал времени, спустя который следует вызывать функцию. Как результат возвращаем другую функцию. Это нужно, чтобы мы могли не менять другие части кода. Чуть позже увидим, как это помогает. В переменной `previousCall` мы храним временную метку предыдущего вызова, а в переменной текущего вызова — временную метку нынешнего момента. Это нужно, чтобы потом сравнить, когда функция была вызвана в этот раз и в предыдущий. Если разница между вызовами меньше, чем указанный интервал, то мы очищаем таймаут, который отвечает за непосредственно вызов функции-аргумента. Обратите внимание, что мы передаём все аргументы `...args`, которые получаем в функции `perform()`. Это тоже нужно, чтобы не приходилось менять другие части кода. Если таймаут был очищен, вызова не произойдёт. Если он не был очищен, то вызовется `callee`. Таким образом, мы как бы «отодвигаем» вызов `callee` до тех пор, пока «снаружи всё не подуспокоится».
+Поехали. Аргументами будут функция, которую надо «откладывать» (`callback`), и интервал времени, спустя который следует вызывать функцию (`delay`). Как результат возвращаем другую функцию, которая является оберткой над `setTimeout` с переданной `callback` функцией.
+
+Обратите внимание, что мы передаём все аргументы `...args`, которые получаем в возвращаемой функции `perform`. Это нужно, чтобы передавать функцию `callback` с любыми аргументами. Если таймаут был очищен, вызова не произойдёт. Если он не был очищен, то вызовется `callback`. Таким образом, мы как бы «отодвигаем» вызов `callback` до тех пор, пока «снаружи всё не подуспокоится».
```javascript
-function debounce(callee, timeoutMs) {
+function debounce(callback, delay) {
+ let timer
return function perform(...args) {
- let previousCall = this.lastCall
-
- this.lastCall = Date.now()
-
- if (previousCall && this.lastCall - previousCall <= timeoutMs) {
- clearTimeout(this.lastCallTimer)
- }
-
- this.lastCallTimer = setTimeout(() => callee(...args), timeoutMs)
+ clearTimeout(timer)
+ timer = setTimeout(() => { callback.apply(this, args) }, delay)
}
}
```
-Использовать такой `debounce()` мы можем так:
+Как работает функция `perform`:
+
+1. При вызове функции `perform`, сначала выполняется `clearTimeout(timer)`. Так удаляется ранее установленный таймер, если он существует. Это необходимо, чтобы предотвратить выполнение предыдущего вызова функции `callback`, если новый вызов произошёл до завершения задержки.
+1. Устанавливается новый таймер с помощью `setTimeout`, и ссылка на этот таймер сохраняется в переменной `timer`.
+1. В `setTimeout` первым аргументом передаётся стрелочная функция с вызовом нашего `callback` c привязкой контектса и передачей всех аргументов. Вторым аргументом передаётся задержка `delay`.
+
+Отметим несколько важных моментов:
+
+- возвращаемая функция `perform` НЕ должна быть стрелочной, чтобы не терялся контекст;
+- чтобы `callback` функция, передаваемая в `setTimeout`, не потеряла контекст, его нужно привязать с помощью `apply`. Это важно при работе с обработчиком событий браузера.
+
+Использовать `debounce()` мы можем так:
```javascript
// Функция, которую хотим «откладывать»
@@ -271,7 +278,7 @@ debouncedDoSomething(42)
## Применяем `debounce()`
-Теперь мы можем применить `debounce()` в нашем обработчике. Сперва немного порефакторим. Вынесем обработчик события в отдельную функцию. Внутри она будет такой же, но так нам удобнее оборачивать её в `debounce()`.
+Теперь применим `debounce()` в нашем обработчике. Сперва немного порефакторим. Вынесем обработчик события в отдельную функцию. Внутри она будет такой же, но так нам удобнее оборачивать её в `debounce()`.
```javascript
function handleInput(e) {
@@ -291,7 +298,7 @@ function handleInput(e) {
searchInput.addEventListener('input', handleInput)
```
-Теперь обернём вынесенную функцию и обновим `addEventListener`. Укажем, что нам нужно ждать 250 мс, прежде чем запустить обработчик. Дальше передаём новую `debounced`-функцию в `addEventListener`.
+Обернём вынесенную функцию и обновим `addEventListener`. Укажем, что нам нужно ждать 250 мс, прежде чем запустить обработчик. Дальше передаём новую `debounced`-функцию в `addEventListener`.
```javascript
function handleInput(e) {
@@ -309,12 +316,10 @@ searchInput.addEventListener('input', debouncedHandle)
Вместо пяти запросов теперь отправляем всего один!
-Обратите внимание, что API функции не поменялось. Мы как передавали [`event`](/js/event/), так и передаём. То есть для внешнего мира debounced-функция ведёт себя точно так же, как и простая функция-обработчик.
-
-Это удобно, потому что меняется лишь одна небольшая часть программы, не затрагивая системы в целом.
+Обратите внимание, что API функции не поменялось. Мы как передавали [`event`](/js/event/), так и передаём. То есть для внешнего мира debounced-функция ведёт себя точно так же, как и простая функция-обработчик. Это удобно, потому что меняется лишь одна небольшая часть программы, не затрагивая системы в целом.
### Результат
Полный пример строки поиска у нас получится такой:
-
+