Я читал книгу Красноречивый JS. В частности, эта глава https://eloquentjavascript.net/15_event.html о различных событиях DOM.
Книга аннотирована примерами кода, но прежде чем их читать, я люблю попробовать воссоздать их самостоятельно, а потом сравнить с более «изящным» решением автора :)
Один из примеров касался темы «отмена дребезга событий». Такая ситуация возникает, когда какое-то событие (например, input
для текстового элемента или scroll
) срабатывает слишком много раз, и мы должны подождать, прежде чем его обработать, иначе мы создадим ненужные промежуточные вычисления.
Давайте проиллюстрируем проблему в примере кода ниже:
<textarea>Write something...</textarea> <script> let text = document.querySelector("textarea"); text.addEventHandler("input", () => { setTimeout(() => { console.log("Send data to server"); }, 3000); }); </script>
Что делает этот код? Или хотя бы должен был сделать? Пользователь введет что-то в текстовое поле, затем программа подождет 3 секунды и отправит данные на сервер.
Но на самом деле этот код работает иначе — он запланирует столько вызовов анонимных функций, сколько раз было запущено событие input
, возможно, много раз…
Чтобы решить эту проблему, нам нужно найти способ подождать, пока пользователь перестанет печатать в течение 3 секунд, и только после этого выполнять функцию. Для этого нам нужно использовать возвращаемое значение из метода setTimeout
. Который мы затем передадим методу clearTimeout
и код будет работать корректно (наконец-то).
Итак, вот моя версия этого кода, которую я придумал до просмотра авторской версии:
let textarea = document.querySelector("textarea"); let dates = []; // stores 2 dates [older_date, newer_date] function updateDates() { dates.push(Date.now()); if (dates.length > 2) { dates.shift(); } } function tooLittleTimePassed(dates, waitTime) { if (dates.length <= 1) { return false; } else { return (dates[1] - dates[0]) < waitTime; } } let waitTime = 3000; textarea.addEventListener("input", event => { updateDates(); let timeoutId = setTimeout(() => { console.log("Data is sent to server"); }, waitTime); if (tooLittleTimePassed(dates, waitTime)) { clearTimeout(timeoutId); } });
Что делает этот код:
- Прослушивает
input
событий - После получения события мы обновляем наш массив
dates
, чтобы он содержал 2 даты, представляющие разницу во времени междуinput
событиями. - Мы планируем функцию через
setTimeout
- Мы проверяем, не слишком ли мала разница во времени между пользовательским вводом, чтобы мы могли решить, нужно ли нам отменить таймер или нет.
ОК, круто. Это моя логика, как бы я подошел к проблеме «устранения дребезга событий». Потом посмотрел авторский вариант решения:
let textarea = document.querySelector("textarea"); let timeout; textarea.addEventListener("input", () => { clearTimeout(timeout); timeout = setTimeout(() => console.log("Typed!"), 500); });
ВТФ? Всего 5 строчек кода... Его версия кода намного элегантнее и надежнее моей. Но теперь я знаю и его технику кунг-фу.
Печально то, что у многих разработчиков нет такой возможности посмотреть на свой кусок кода, а затем сравнить его с другой, более элегантной версией (написанной кем-то другим). Должны ли мы чаще использовать такие сервисы, как https://codereview.stackexchange.com/? Что, если человек пишет код как разработчик-одиночка, и эта кодовая база является частной. Он обречен писать неоптимальный код?