How to write a throttle function | Writing a throttle function in Javascript

Поделиться
HTML-код
  • Опубликовано: 23 май 2020
  • Продолжаем серию видео по вашим вопросам. Сегодняшний выпуск про функцию Throttle.
    Функция Throttle достаточно часто используется в случаях, когда необходимо игнорировать частые вызовы какой-то функции. Например, при ресайзе окна браузера, или движения мышки генерируется очень много браузерных событий. Если функция, которая обрабатывает эти события, достаточно медленная, то интерфейс будет "притормаживать". Throttle поможет сократить количество вызовов функции обработчика, что сделает интерфейс приложения более отзывчивым. Она позволяет сделать так, чтобы функция вызывалась не чаще, чем один раз в N милисекунд.
    Также это частый вопрос на фронтенд-собеседованиях. Приятного просмотра!
    Код из видео: codepen.io/puzankov/pen/rNOqo...
    Рекомендуем также посмотреть видео про функцию Debounce: • Как написать функцию d...
    ---
    Если видео было для вас полезным, ставьте лайк и поделитесь им с друзьями.
    Подписывайтесь на наш канал: bit.ly/fs-ytb
    ---
    Присоединяйтесь к нам в соцсетях:
    FB: / frontendscience
    Instagram Сергея Пузанкова: / puzankovcom
    Заходите на наш сайт: frontend-science.com/
    #throttle, #javascript, #frontend

Комментарии • 71

  • @HOTBOY9961
    @HOTBOY9961 4 года назад +3

    открыл для себя еще один отличный канал, спасибо за контент, все на уровне!

    • @frontendscience
      @frontendscience  4 года назад +1

      Вам спасибо, что смотрите!

  • @user-md5mw1tp3e
    @user-md5mw1tp3e 4 года назад +3

    Спасибо большое, что помогаете глубже познать JS.

  • @wishmaker1443
    @wishmaker1443 2 года назад

    Спасибо огромное, Сергей! Все понятно и без лишних слов.

  • @user-rp9po1ev5r
    @user-rp9po1ev5r Год назад

    Cпасибо за контент! Теперь надо запомнить и тренироваться больше, чтоб написать самостоятельно на собесе)☺

  • @SovMan
    @SovMan 4 года назад +4

    Крутяк!!! Спасибо!

    • @smashno
      @smashno 4 года назад +1

      Благодарю!

  • @kri4evskiy
    @kri4evskiy 3 года назад +3

    ...ну и соответственно лайк и коммент не глядя ;))

    • @frontendscience
      @frontendscience  3 года назад

      Лайк и сердечко не глядя! 😉

  • @darkcasper1488
    @darkcasper1488 Год назад +6

    хороший видос, но музыка слишком громкая, сбивает мысли для понимания

  • @J-Lany
    @J-Lany 4 месяца назад

    Привет! Большое спасибо за видео, смотрю второе подряд (начилана с дебаунса), и уже подписалась! Максимально разжевано, понятно и не монтоно (а это оч важно) :)
    Остался ток вопросец, почему юзаем не стрелочную функцию с эпплай, а не стрелочную (вот тут не совсем уловила), такой же вопрос и про видео с дебаунсом.

  • @nnnabbot
    @nnnabbot Год назад

    За видео спасибо, и все ясно когда тебе рассказывают. Но смогу ли сам потом написать? - большой вопрос :-)

  • @artem_bohak
    @artem_bohak Год назад

    Спасибо Вам большое за видео! Можете подсказать, что Вы за шрифт используете в редакторе?

  • @grantorino3465
    @grantorino3465 2 года назад

    тоже в работе использую такие обертки, особенно когда функция выполняет запросы к серверу по изменению сильно часто меняющихся событий, типа движения мыши, скролл, изменение значения input типа range и т.д. но использовал такую реализацию, которую когда-то нагуглил
    waitForFinalEvent: (() => {
    const timers = {}
    return (callback, ms, uniqueId) => {
    if (!uniqueId) {
    uniqueId = 'Don`t call this twice without a uniqueId'
    }
    if (timers[uniqueId]) {
    clearTimeout(timers[uniqueId])
    }
    timers[uniqueId] = setTimeout(callback, ms)
    }
    })()

  • @relaxnature6649
    @relaxnature6649 2 года назад +3

    Паттерны проектирования которые используются на практике хотелось бы увидеть.

  • @livevikulin
    @livevikulin Год назад +3

    Инфа годная, но сделай, пожалуйста, музыку потише)))

  • @boryskatowsky5190
    @boryskatowsky5190 2 месяца назад

    Очень полезное видео. А у Вас нет случайно урока где такую же функцию можно реализовать с использованием ts?

  • @EvilYou
    @EvilYou 2 года назад

    Сегодня повторял декораторы, написал такую функцию для throttle.
    function throttle(f, ms) {
    let isThrottled = false;
    let savedThis;
    let savedArgs;
    let isWaiting = false;
    return function inner() {
    if (isThrottled) {
    savedThis = this;
    savedArgs = arguments;
    isWaiting = true;
    return;
    }
    if (!isWaiting) {
    savedThis = this;
    savedArgs = arguments;
    }
    isWaiting = false;
    isThrottled = true;
    setTimeout(() => {
    isThrottled = false;
    if (isWaiting) inner();
    }, ms);
    return f.apply(savedThis, savedArgs);
    }
    }
    Использовал дополнительную переменную, тк думал о случае, когда функция может вызываться без аргументов, но не учел, что тогда в savedArgs будет пустой массив, а не undefined :)

  • @user-ut4uk9mm8c
    @user-ut4uk9mm8c 3 года назад +2

    неплохой способ, но как-то сложноватый. И подтормаживает. Я нашел другое решение, которое к тому же работает быстрее
    но здесь идет сравнение по времени)))
    function throttle(fn, delay) {
    let lastMoment = 0;
    return (...args) => {
    const nowMoment = new Date().getTime();
    if (nowMoment - lastMoment < delay) {
    return;
    }
    lastMoment = nowMoment;
    return fn(...args);
    };
    }
    let showCoord = (e) => {
    console.log(` X=${e.clientX} Y=${e.clientY}`);
    };
    showCoord = throttle(showCoord, 50);
    document.addEventListener("mousemove", showCoord);

    • @frontendscience
      @frontendscience  3 года назад +11

      Комментарий из разряда «видео не смотрел, пишу комменты». Твое «проще» это отсутствие части функциональности, которая должна быть для корректной обработки всех ивентов. И ты узнал бы об этом, если бы досмотрел видео до конца.
      И что значит «тормозит»? Ты в курсе, что тротл это и есть тормозилка?
      И да, твой урезанный вариант будет быстрее, но он неправильный.

  • @andreygokhan6893
    @andreygokhan6893 3 года назад +1

    Спасибо. Полезная функция. Только возникает вопрос: Нужно ли переназначать savedThis на каждом событии, а потом в таймере обnullять? Может достаточно присвоить один раз. Просто не могу представить кейс в котором вдруг поменялся this.

    • @frontendscience
      @frontendscience  3 года назад +2

      Все дело в том что сама эта функция она универсальна и может использоваться где угодно. Вполне может быть что одна и та же функция будет отрабатывать на разные ивенты, поэтому мы не можем знать кокой this будет. В примере с scroll window -действительно this не меняется.

  • @andranikmeneshyan5829
    @andranikmeneshyan5829 4 года назад +2

    Канал норм) только вот на строке 21 как минимум ошибка должна была быть ))) Монтаж Монтажович)

    • @frontendscience
      @frontendscience  4 года назад +4

      Весь код всегда пишется вживую. В этот раз appply был исправлен во время запуска, фотошопа здесь нет. Ну и это не ливстриминг, чтобы показывать процесс исправления всех опечаток на камеру. Рабочий код можете сверить с кодом в видео - они идентичны, ссылка в описании. Приятного просмотра!

  • @ProoksiusSvaagali
    @ProoksiusSvaagali 6 месяцев назад

    У меня покороче вот получилось немного. Проверил - вроде работает, и последний раз вызывается.
    function getTrottle(fn, time) {
    let newArgs
    let newThis
    setInterval(() => {
    if (newArgs !== undefined) {
    fn.apply(newThis, newArgs)
    newArgs = undefined
    newThis = undefined
    }
    }, time)
    return function (...args) {
    newArgs = args
    newThis = this
    }
    }

  • @ilyaprotsenko1023
    @ilyaprotsenko1023 Год назад

    Привіт! Так там же контекст буде undefined. В такому випадку ми можемо прибрати одну змінну - savedThis.
    Якщо помиляюся чи чогось не зрозумів, будь ласка, виправьте мене :)
    Дякую!
    function throttle(cb, timeMs) {
    let isThrottled = false;
    let savedArgs = null;
    function wrapper(...args) {
    if (isThrottled) {
    console.log("isThrottled this", this);
    savedArgs = args;
    return;
    }
    cb(...args);
    isThrottled = true;
    setTimeout(function () {
    isThrottled = false;
    if (savedArgs) {
    wrapper(...savedArgs);
    savedArgs = null;
    }
    }, timeMs);
    }
    return wrapper;
    }

  • @artemshishkin9055
    @artemshishkin9055 2 года назад

    я может пропустил, почему в итоге wrapper возвращаем (return'им) потом отдельно в конце, а не сразу в момент обь'явления wrapper'a?

    • @frontendscience
      @frontendscience  2 года назад

      Для простоты объяснения и понимания работы кода.

    • @diamondskrt
      @diamondskrt Год назад +1

      ты ничего не пропустил) в действительности можно без проблем возвращать в момент объявления. при этом простота объяснения остается в силе и код не становится от этого как-то сложнее понимать)

  • @ReaktorGaming86
    @ReaktorGaming86 2 дня назад

    Как написать тоже самое на реакте?

  • @imbydlo1552
    @imbydlo1552 3 года назад +2

    Вот не могу понять работа кода... Ведь и последний аргумент тоже должен же обнулится и ничего на дать? Или как?

    • @frontendscience
      @frontendscience  3 года назад +1

      Если прямо сейчас мы не выполняем функцию коллбек - то мы сохраняем значение аргументов в переменную savedArgs. И запускаем sеtTimeout - который выполнит колбек с нашими сохраненными аргументами через сколько-то милисекунд таймаута

  • @relaxation4237
    @relaxation4237 Год назад +4

    Музыка тут лишняя

  • @antonarbus
    @antonarbus 3 года назад +1

    Объясните пожалуйста кто-нибудь про переменную "arguments". Из MDN читаю "Объект arguments доступен внутри любой (нестрелочной) функции и содержит аргументы, переданные в функцию."
    По идее в строках 9 и 14 объект "arguments" относится к функции "wrapper" и должен содержать ее аргументы, но ведь нет же, он каким-то образом содержит аргументы функции "func". Явно я не понимаю какую-то базовую основу.

    • @andreygokhan6893
      @andreygokhan6893 3 года назад +1

      Так мы же сами передаём arguments в func через метод apply, а там все аргументы из wrapper. Сам я использую спред, чтобы заглянувших в мой код инопланетян не смутил не пойми откуда взявшийся arguments.

    • @frontendscience
      @frontendscience  3 года назад +3

      Ты верно заметил про строки 9 и 14. Ключевой момент заключается в том, что мы в самом конце возвращаем функцию wrapper. То-есть раньше ты на ресайз окна вызывал например onWindowResize, а теперь ты обернул ее в функцию троттл и по факту вернул wrapper (который внутри себя вызывает onWindowResize) - Но при ресайзе окна вызывается именно wrapper - и в неё предаются различные аргументы, которые мы просто проксируем в оригинальную функцию func.

    • @frontendscience
      @frontendscience  3 года назад +1

      😂инопланетяне в безопасности!

    • @antonarbus
      @antonarbus 3 года назад

      @@frontendscience спасибо

    • @antonarbus
      @antonarbus 3 года назад

      @@andreygokhan6893 спасибо

  • @grantorino3465
    @grantorino3465 2 года назад

    разве так называемый паттерн враппер не называется правильно - декоратор?

    • @frontendscience
      @frontendscience  2 года назад

      Да, так его тоже называют

  • @user-zy2pc5tg3q
    @user-zy2pc5tg3q Год назад

    Відео то корисне, але в чому проблема використовувати debounce?

    • @user-dt9tb2rm7l
      @user-dt9tb2rm7l Год назад

      Можно и без этих функций) Но это не удобно.
      Они по-разному работают. Посмотри примеры использования.

    • @michaelkronenberg3712
      @michaelkronenberg3712 9 месяцев назад

      Разница простая например дебаунс будет неплохо работать с ивентами которые вызываются скажем менее часто такие хттп запросы или кибайнд ивенты, а вот сроттлинг больше подойдет для более тяжелых и частых ивентов например когда юзер водит мышкой по экрану что происходит каждые милисекунды десятки раз и для данного решения подойдет сротллинг потому что он является более оптимизированным решением.

  • @astrotrain
    @astrotrain 4 года назад +3

    appply ((:

    • @smashno
      @smashno 4 года назад +8

      Установлено - чем больше "p" внутри apply - тем лучше аплаится! :)

  • @romanbush5164
    @romanbush5164 Год назад

    Хех кастыльный rxjs

  • @artyomnelyub9591
    @artyomnelyub9591 2 года назад +2

    Подскажите, пожалуйста, кто-нибудь, в чем принципиальное отличие от debounce из предыдущего выпуска ? (ruclips.net/video/YaM0CaDTshc/видео.html&ab_channel=Front-endSciencec%D0%A1%D0%B5%D1%80%D0%B3%D0%B5%D0%B5%D0%BC%D0%9F%D1%83%D0%B7%D0%B0%D0%BD%D0%BA%D0%BE%D0%B2%D1%8B%D0%BC)
    Здесь говорится, что функция должна отработать последний вызов, даже если он был проигнорирован. Но, в предыдущем примере про debounce, последний вызов все равно выполнится. В чем разница?

    • @vadavur
      @vadavur 2 года назад +6

      Debounce выполняет функцию только тогда, когда прекращается ее постоянный повторяющийся вызов, - прекращается хотя бы на указанный delay time. А throttle выполняет функцию сразу, плюс, при постоянно повторяющемся вызове не чаще, чем один раз в delay time, и обязательно последний вызов.
      То есть, например, если delay = 1000ms и быстро фигачить по кнопке отправить, то при debounce отправлено будет только когда перестанешь фигачить или хотя бы сделаешь перерыв на 1000ms, а при throttle будет отправлено сразу, потом будет отправляться каждые 1000ms (ну, если не учитывать дополнительные микрозадержки), а потом еще в конце, когда перестанешь фигачить по "отправить", обязательно последний вызов функции отработает

  • @cikada3398
    @cikada3398 Год назад

    apply тут роли не играет

  • @kri4evskiy
    @kri4evskiy 3 года назад +2

    А производительность сильно просаживается от такой функции?

    • @frontendscience
      @frontendscience  3 года назад +6

      Нет! На производительность это влияет только в положительную сторону, так как мы не вызываем постоянно тяжелый колбек. Сам врапер просадки особой не дает.

    • @kri4evskiy
      @kri4evskiy 3 года назад +2

      @@frontendscience Понял, спасибо за пояснение!

  • @ndrey3848
    @ndrey3848 3 года назад

    1:55 Ваши интерфейсы рассчитаны на сверхлюдей, которые могут увидеть разницу в 0,01с? Адекватный заказчик вряд ли захочет платить за такую ...

    • @frontendscience
      @frontendscience  3 года назад +5

      1) Вопрос здесь не в том, сколько мс проходит между самими событиями, а в том, что иногда отрисовка интерфейса может занимать секунды. И если ты на каждый ивент, сгенерированный браузером, будешь делать отрисовку, у пользователя просто зависнет браузер. Именно за такие оптимизации адекватные заказчики и готовы платить деньги.
      2) Старайся формулировать нормальным тоном свои вопросы. Иначе уйдешь в бан.

    • @ndrey3848
      @ndrey3848 3 года назад

      @@frontendscience 1) С первой частью полностью согласен, не стоит обрабатывать все ивенты. Я кроме времени обработки ограничиваю и координаты, чтобы не рендерить разницу в каждый пиксель. Просто я обычно не заморачиваюсь с последней перерисовкой.
      2) Не хотел обидеть, просто пример в 10мс был не самый удачный (ИМХО). Канал хороший, спасибо за годный контент :)

  • @szCerber
    @szCerber Год назад +2

    21 строка aPPPly ? 0о