Я читал книгу Красноречивый 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);
    }
});

Что делает этот код:

  1. Прослушивает input событий
  2. После получения события мы обновляем наш массив dates, чтобы он содержал 2 даты, представляющие разницу во времени между input событиями.
  3. Мы планируем функцию через setTimeout
  4. Мы проверяем, не слишком ли мала разница во времени между пользовательским вводом, чтобы мы могли решить, нужно ли нам отменить таймер или нет.

ОК, круто. Это моя логика, как бы я подошел к проблеме «устранения дребезга событий». Потом посмотрел авторский вариант решения:

let textarea = document.querySelector("textarea");
  let timeout;
  textarea.addEventListener("input", () => {
    clearTimeout(timeout);
    timeout = setTimeout(() => console.log("Typed!"), 500);
  });

ВТФ? Всего 5 строчек кода... Его версия кода намного элегантнее и надежнее моей. Но теперь я знаю и его технику кунг-фу.

Печально то, что у многих разработчиков нет такой возможности посмотреть на свой кусок кода, а затем сравнить его с другой, более элегантной версией (написанной кем-то другим). Должны ли мы чаще использовать такие сервисы, как https://codereview.stackexchange.com/? Что, если человек пишет код как разработчик-одиночка, и эта кодовая база является частной. Он обречен писать неоптимальный код?