Привет! Большое спасибо за видео, смотрю второе подряд (начилана с дебаунса), и уже подписалась! Максимально разжевано, понятно и не монтоно (а это оч важно) :) Остался ток вопросец, почему юзаем не стрелочную функцию с эпплай, а не стрелочную (вот тут не совсем уловила), такой же вопрос и про видео с дебаунсом.
Спасибо. Полезная функция. Только возникает вопрос: Нужно ли переназначать savedThis на каждом событии, а потом в таймере обnullять? Может достаточно присвоить один раз. Просто не могу представить кейс в котором вдруг поменялся this.
Все дело в том что сама эта функция она универсальна и может использоваться где угодно. Вполне может быть что одна и та же функция будет отрабатывать на разные ивенты, поэтому мы не можем знать кокой this будет. В примере с scroll window -действительно this не меняется.
тоже в работе использую такие обертки, особенно когда функция выполняет запросы к серверу по изменению сильно часто меняющихся событий, типа движения мыши, скролл, изменение значения 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) } })()
Если прямо сейчас мы не выполняем функцию коллбек - то мы сохраняем значение аргументов в переменную savedArgs. И запускаем sеtTimeout - который выполнит колбек с нашими сохраненными аргументами через сколько-то милисекунд таймаута
У меня покороче вот получилось немного. Проверил - вроде работает, и последний раз вызывается. 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 } }
Объясните пожалуйста кто-нибудь про переменную "arguments". Из MDN читаю "Объект arguments доступен внутри любой (нестрелочной) функции и содержит аргументы, переданные в функцию." По идее в строках 9 и 14 объект "arguments" относится к функции "wrapper" и должен содержать ее аргументы, но ведь нет же, он каким-то образом содержит аргументы функции "func". Явно я не понимаю какую-то базовую основу.
Так мы же сами передаём arguments в func через метод apply, а там все аргументы из wrapper. Сам я использую спред, чтобы заглянувших в мой код инопланетян не смутил не пойми откуда взявшийся arguments.
Ты верно заметил про строки 9 и 14. Ключевой момент заключается в том, что мы в самом конце возвращаем функцию wrapper. То-есть раньше ты на ресайз окна вызывал например onWindowResize, а теперь ты обернул ее в функцию троттл и по факту вернул wrapper (который внутри себя вызывает onWindowResize) - Но при ресайзе окна вызывается именно wrapper - и в неё предаются различные аргументы, которые мы просто проксируем в оригинальную функцию func.
ты ничего не пропустил) в действительности можно без проблем возвращать в момент объявления. при этом простота объяснения остается в силе и код не становится от этого как-то сложнее понимать)
Разница простая например дебаунс будет неплохо работать с ивентами которые вызываются скажем менее часто такие хттп запросы или кибайнд ивенты, а вот сроттлинг больше подойдет для более тяжелых и частых ивентов например когда юзер водит мышкой по экрану что происходит каждые милисекунды десятки раз и для данного решения подойдет сротллинг потому что он является более оптимизированным решением.
неплохой способ, но как-то сложноватый. И подтормаживает. Я нашел другое решение, которое к тому же работает быстрее но здесь идет сравнение по времени))) 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);
Комментарий из разряда «видео не смотрел, пишу комменты». Твое «проще» это отсутствие части функциональности, которая должна быть для корректной обработки всех ивентов. И ты узнал бы об этом, если бы досмотрел видео до конца. И что значит «тормозит»? Ты в курсе, что тротл это и есть тормозилка? И да, твой урезанный вариант будет быстрее, но он неправильный.
Сегодня повторял декораторы, написал такую функцию для 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 :)
Весь код всегда пишется вживую. В этот раз appply был исправлен во время запуска, фотошопа здесь нет. Ну и это не ливстриминг, чтобы показывать процесс исправления всех опечаток на камеру. Рабочий код можете сверить с кодом в видео - они идентичны, ссылка в описании. Приятного просмотра!
Привіт! Так там же контекст буде 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; }
Подскажите, пожалуйста, кто-нибудь, в чем принципиальное отличие от debounce из предыдущего выпуска ? (ua-cam.com/video/YaM0CaDTshc/v-deo.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, последний вызов все равно выполнится. В чем разница?
Debounce выполняет функцию только тогда, когда прекращается ее постоянный повторяющийся вызов, - прекращается хотя бы на указанный delay time. А throttle выполняет функцию сразу, плюс, при постоянно повторяющемся вызове не чаще, чем один раз в delay time, и обязательно последний вызов. То есть, например, если delay = 1000ms и быстро фигачить по кнопке отправить, то при debounce отправлено будет только когда перестанешь фигачить или хотя бы сделаешь перерыв на 1000ms, а при throttle будет отправлено сразу, потом будет отправляться каждые 1000ms (ну, если не учитывать дополнительные микрозадержки), а потом еще в конце, когда перестанешь фигачить по "отправить", обязательно последний вызов функции отработает
Нет! На производительность это влияет только в положительную сторону, так как мы не вызываем постоянно тяжелый колбек. Сам врапер просадки особой не дает.
1) Вопрос здесь не в том, сколько мс проходит между самими событиями, а в том, что иногда отрисовка интерфейса может занимать секунды. И если ты на каждый ивент, сгенерированный браузером, будешь делать отрисовку, у пользователя просто зависнет браузер. Именно за такие оптимизации адекватные заказчики и готовы платить деньги. 2) Старайся формулировать нормальным тоном свои вопросы. Иначе уйдешь в бан.
@@frontendscience 1) С первой частью полностью согласен, не стоит обрабатывать все ивенты. Я кроме времени обработки ограничиваю и координаты, чтобы не рендерить разницу в каждый пиксель. Просто я обычно не заморачиваюсь с последней перерисовкой. 2) Не хотел обидеть, просто пример в 10мс был не самый удачный (ИМХО). Канал хороший, спасибо за годный контент :)
открыл для себя еще один отличный канал, спасибо за контент, все на уровне!
Вам спасибо, что смотрите!
Спасибо большое, что помогаете глубже познать JS.
Рады, что было полезно!
Спасибо огромное, Сергей! Все понятно и без лишних слов.
Очень рад! 👍
Крутяк!!! Спасибо!
Благодарю!
...ну и соответственно лайк и коммент не глядя ;))
Лайк и сердечко не глядя! 😉
Привет! Большое спасибо за видео, смотрю второе подряд (начилана с дебаунса), и уже подписалась! Максимально разжевано, понятно и не монтоно (а это оч важно) :)
Остался ток вопросец, почему юзаем не стрелочную функцию с эпплай, а не стрелочную (вот тут не совсем уловила), такой же вопрос и про видео с дебаунсом.
Cпасибо за контент! Теперь надо запомнить и тренироваться больше, чтоб написать самостоятельно на собесе)☺
Очень полезное видео. А у Вас нет случайно урока где такую же функцию можно реализовать с использованием ts?
Спасибо Вам большое за видео! Можете подсказать, что Вы за шрифт используете в редакторе?
Как написать тоже самое на реакте?
За видео спасибо, и все ясно когда тебе рассказывают. Но смогу ли сам потом написать? - большой вопрос :-)
Паттерны проектирования которые используются на практике хотелось бы увидеть.
Спасибо. Полезная функция. Только возникает вопрос: Нужно ли переназначать savedThis на каждом событии, а потом в таймере обnullять? Может достаточно присвоить один раз. Просто не могу представить кейс в котором вдруг поменялся this.
Все дело в том что сама эта функция она универсальна и может использоваться где угодно. Вполне может быть что одна и та же функция будет отрабатывать на разные ивенты, поэтому мы не можем знать кокой this будет. В примере с scroll window -действительно this не меняется.
тоже в работе использую такие обертки, особенно когда функция выполняет запросы к серверу по изменению сильно часто меняющихся событий, типа движения мыши, скролл, изменение значения 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)
}
})()
Вот не могу понять работа кода... Ведь и последний аргумент тоже должен же обнулится и ничего на дать? Или как?
Если прямо сейчас мы не выполняем функцию коллбек - то мы сохраняем значение аргументов в переменную savedArgs. И запускаем sеtTimeout - который выполнит колбек с нашими сохраненными аргументами через сколько-то милисекунд таймаута
хороший видос, но музыка слишком громкая, сбивает мысли для понимания
У меня покороче вот получилось немного. Проверил - вроде работает, и последний раз вызывается.
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
}
}
Объясните пожалуйста кто-нибудь про переменную "arguments". Из MDN читаю "Объект arguments доступен внутри любой (нестрелочной) функции и содержит аргументы, переданные в функцию."
По идее в строках 9 и 14 объект "arguments" относится к функции "wrapper" и должен содержать ее аргументы, но ведь нет же, он каким-то образом содержит аргументы функции "func". Явно я не понимаю какую-то базовую основу.
Так мы же сами передаём arguments в func через метод apply, а там все аргументы из wrapper. Сам я использую спред, чтобы заглянувших в мой код инопланетян не смутил не пойми откуда взявшийся arguments.
Ты верно заметил про строки 9 и 14. Ключевой момент заключается в том, что мы в самом конце возвращаем функцию wrapper. То-есть раньше ты на ресайз окна вызывал например onWindowResize, а теперь ты обернул ее в функцию троттл и по факту вернул wrapper (который внутри себя вызывает onWindowResize) - Но при ресайзе окна вызывается именно wrapper - и в неё предаются различные аргументы, которые мы просто проксируем в оригинальную функцию func.
😂инопланетяне в безопасности!
@@frontendscience спасибо
@@andreygokhan6893 спасибо
я может пропустил, почему в итоге wrapper возвращаем (return'им) потом отдельно в конце, а не сразу в момент обь'явления wrapper'a?
Для простоты объяснения и понимания работы кода.
ты ничего не пропустил) в действительности можно без проблем возвращать в момент объявления. при этом простота объяснения остается в силе и код не становится от этого как-то сложнее понимать)
Відео то корисне, але в чому проблема використовувати debounce?
Можно и без этих функций) Но это не удобно.
Они по-разному работают. Посмотри примеры использования.
Разница простая например дебаунс будет неплохо работать с ивентами которые вызываются скажем менее часто такие хттп запросы или кибайнд ивенты, а вот сроттлинг больше подойдет для более тяжелых и частых ивентов например когда юзер водит мышкой по экрану что происходит каждые милисекунды десятки раз и для данного решения подойдет сротллинг потому что он является более оптимизированным решением.
неплохой способ, но как-то сложноватый. И подтормаживает. Я нашел другое решение, которое к тому же работает быстрее
но здесь идет сравнение по времени)))
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);
Комментарий из разряда «видео не смотрел, пишу комменты». Твое «проще» это отсутствие части функциональности, которая должна быть для корректной обработки всех ивентов. И ты узнал бы об этом, если бы досмотрел видео до конца.
И что значит «тормозит»? Ты в курсе, что тротл это и есть тормозилка?
И да, твой урезанный вариант будет быстрее, но он неправильный.
Ты монстр джаваскрипта 😅
Инфа годная, но сделай, пожалуйста, музыку потише)))
разве так называемый паттерн враппер не называется правильно - декоратор?
Да, так его тоже называют
Сегодня повторял декораторы, написал такую функцию для 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 :)
Канал норм) только вот на строке 21 как минимум ошибка должна была быть ))) Монтаж Монтажович)
Весь код всегда пишется вживую. В этот раз appply был исправлен во время запуска, фотошопа здесь нет. Ну и это не ливстриминг, чтобы показывать процесс исправления всех опечаток на камеру. Рабочий код можете сверить с кодом в видео - они идентичны, ссылка в описании. Приятного просмотра!
Привіт! Так там же контекст буде 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;
}
Подскажите, пожалуйста, кто-нибудь, в чем принципиальное отличие от debounce из предыдущего выпуска ? (ua-cam.com/video/YaM0CaDTshc/v-deo.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, последний вызов все равно выполнится. В чем разница?
Debounce выполняет функцию только тогда, когда прекращается ее постоянный повторяющийся вызов, - прекращается хотя бы на указанный delay time. А throttle выполняет функцию сразу, плюс, при постоянно повторяющемся вызове не чаще, чем один раз в delay time, и обязательно последний вызов.
То есть, например, если delay = 1000ms и быстро фигачить по кнопке отправить, то при debounce отправлено будет только когда перестанешь фигачить или хотя бы сделаешь перерыв на 1000ms, а при throttle будет отправлено сразу, потом будет отправляться каждые 1000ms (ну, если не учитывать дополнительные микрозадержки), а потом еще в конце, когда перестанешь фигачить по "отправить", обязательно последний вызов функции отработает
Музыка тут лишняя
А производительность сильно просаживается от такой функции?
Нет! На производительность это влияет только в положительную сторону, так как мы не вызываем постоянно тяжелый колбек. Сам врапер просадки особой не дает.
@@frontendscience Понял, спасибо за пояснение!
1:55 Ваши интерфейсы рассчитаны на сверхлюдей, которые могут увидеть разницу в 0,01с? Адекватный заказчик вряд ли захочет платить за такую ...
1) Вопрос здесь не в том, сколько мс проходит между самими событиями, а в том, что иногда отрисовка интерфейса может занимать секунды. И если ты на каждый ивент, сгенерированный браузером, будешь делать отрисовку, у пользователя просто зависнет браузер. Именно за такие оптимизации адекватные заказчики и готовы платить деньги.
2) Старайся формулировать нормальным тоном свои вопросы. Иначе уйдешь в бан.
@@frontendscience 1) С первой частью полностью согласен, не стоит обрабатывать все ивенты. Я кроме времени обработки ограничиваю и координаты, чтобы не рендерить разницу в каждый пиксель. Просто я обычно не заморачиваюсь с последней перерисовкой.
2) Не хотел обидеть, просто пример в 10мс был не самый удачный (ИМХО). Канал хороший, спасибо за годный контент :)
apply тут роли не играет
Хех кастыльный rxjs
21 строка aPPPly ? 0о