Пишем вместе throttle и debounce | Уроки JavaScript
Vložit
- čas přidán 25. 07. 2024
- Разберём зачем нужны функции throttle и debounce, вместе реализуем и разберём различия.
🍀 Поддержать канал: www.donationalerts.com/r/webe...
☕️ Купить кофе: buy.stripe.com/5kA7sL9574SG7x...
🎨 Купить набор кистей Procreate: webelart.com/illustration.
✍️ Мой telegram channel: t.me/webelart
🏰 Английский CZcams: @webelart_en
💁🏼♀️ Инстаграм: / webelart
🦄 LinkedIn: / webelart
❤️ Пример кода из урока: / shpargalka-51868113 .
00:00 введение.
00:29 debounce
11:16 throttle
На канале я рассматриваю различные темы веб-разработки, на текущий момент: веб-основы, веб-анимации, веб-дизайн.
У тебя очень классные уроки! Cпасибо за то, что ты делаешь 💞💞
❤️❤️❤️
от души душевно в душу, выручила!
Супер! Спасибо!
Было очень интересно!♥️ Спасибо большое за ваш труд! Больше видео по JavaScript♥️
Спасибо за просмотр! 😍❤️
Спасибо ) не знал что это так называется ) решал это несколько по другому
Лайк!
Всем рекомендую❤️ Вы мастер объяснений🥰
Ого, спасибо большое! ❤️
#webelart throttle попробуйте вызвать в строгом режиме и нет,
интересно
что-то измениться?
Подсказка
будет разный результат.
Может лучше на 40 строке сделать saveThis || saveArgs
А так, материал -- супер!
короче, неплохо. Можно даже сказать, хорошо!
круто
Преподаватель от бога:)
Отличный контент, очень целевой, коротко и по делу.
Хорошее видео, был один момент, которого я не знал
Получается debounce() обертка генерит функции, которые юзают один и тот же timer (из замыкания). Мне для каждого случая (когда нужны разные timer) создавать свою debounce() ? Хотелось бы еще разобрать метод, наподобие createDebounce(). Или я вообще не в том направлении думаю?
Интересный вопрос, задумалась над этим. Давайте посмотрим. Приведу примеры, которые я часто использую для debounce, у меня это со всякими resize, change events:
input.addEventListener('change', debounce(updateValue));
window.addEventListener('resize', debounce(updateValue));
Т.е. для каждого случая в этом примере по факту своя обёртка. Здесь наверно встаёт вопрос, что если мы хотим в одну функцию вынести deboundedUpdate = debounce(updateValue) и одновременно использовать эту обёрнутую функцию. То здесь могут быть затирки timer, их не очистка и отрабатывание setTimeout. В целом по ощущениям к каким-то глобальным ошибкам не приведёт, но тем не менее не круто.
Про createDebounce(), не поняла, что имеете ввиду.
Функция в глобальном скоупе. Надо делать объект кмк
Спасибо за урок! Интересная тема и практичная.
Вот мне кажется в debounce в setTimeout можно коллбэк передавать без apply и this. Только саму функцию и аргументы. Может я что-то упустил?!
Можно и без, но с this более широкое решение и вы сможете использовать debounce в случаях даже когда используете контекст внутри функций. Например классах.
объясните пожалуйста, почему debounce в этом примере не срабатывает:
textarea.addEventListener('input', (e) => {
debounce( () => { console.log('1'); }, 2000);
}
Debounce возвращает новую функцию, т.е. вы ничего здесь не вызываете. Вам нужно целиком колбек обернуть, тогда при срабатывание события 'input' на textarea функция будет автоматически вызываться.
const debouncedCallback = debounce((e) => {....}, 2000)
textarea.addEventListener('input', debouncedCallback)
Что-то такое должно получиться.
@@webelart спасибо большое
Вот это
CZcams кажется опять трёт комментарии. Вот эту ссылку скидывали github.com/lukasoppermann/html5sortable/blob/master/src/throttle.ts
Прикольная реализация, мне понравилась. Решение выглядит действительно проще, но здесь есть неучтённый момент. Например, мы делаем эту обёртку и у нас вызывается кучу событий скролла предположим (или других вызовов) и данная функция не даёт гарантии, что последнее значение скролла будет вызвано. Для меня как раз в решении, хотелось эту штуку учесть. Т.е. если последний вызов попал на время, которое ещё не вошло в условие, это значит, что мы потеряли самый последний вызов, а терять его не хочется. Поэтому решение в примере из видео более такое может громоздкое, там сохраняются переменные и потом в таймере вызываются, т.е. всегда последний вызов будет вызван с самыми последними значениями.
@@webelart Я проверял, здесь вызывается последний запрос, в том то и дело. Оно работает так же само, просто меньше кода
@@romanryaboshtan9270 Здесь нужно смотреть разные кейсы. Например, вот этот кейс не работает:
const f = throttle(console.log, 500);
f(1);
f(2);
setTimeout(() => f(3), 100);
setTimeout(() => f(4), 500);
setTimeout(() => f(5), 900);
Вызывается только 1 и 4.
@@webelart да, вы правы, я там поменял условие >= delay + 2 - теперь и этот кейс, который вы указали, работает, вызывается 1, 4 и 5. Я ещё раз проверил, да, здесь есть небольшой баг, ваш вариант лучше, согласен
@@romanryaboshtan9270 Вы сейчас пошутили? о_О Вы же понимаете, что кейсов может быть куча и я к своему примеру тоже +2 могу добавить. Что решения в программировании должны быть универсальными. А код, который вы предоставили не будет работать в задаче которую я описала, я имею ввиду работать в 100% различных кейсов, а не в 3% по случайности. И в примере даже нет написанных строк, которые заставят его работать так, там одно условие и если оно не сработает вызов будет потерян. В моём примере есть setTimeout, который словит последний вызов, обязательно словит и с нужными переменными.
В функции дебаунс я бы все таки сделал, чтобы первый вызов всегда вызывался, добавив булевый флаг... А дальше уже регулировал по таймингу... Но возможно это уже от типа задачи к
Как всегда уроки хорошие, понятно и приятно смотреть ..ум и красота страшная сила 💪 :)
Не совсем понимаю зачем в debounce делаем callback.apply(this) если стрелочная функция и так передает контекст ?
const self = this;
.....
...apply(self, params)
Спасибо, прикольный патерн debounce , буду использовать, кто плохо понимает, как это работает, изучайте колбеки и замыкание ребята)
спасибо, Елена!)
подскажите -- как Вы решаете "проблему ресайза", когда при изменении размера дисплея нужно выполнить функцию (только раз) и вернуть все обратно при изменении в другую строну? debounce замедлит, но данное решение не єлегантное. использую flag, но может есть решение лучше
Прямо, чтобы один раз сработало, из решений здесь по факту нужен debounce и какая-нибудь переменная flag, типа только один раз сработай. Устанавливаете false, вначале и true, как только прошло событие.
Но здесь ещё интересно, что за поведение более подробно, возможно debounce и не нужен. Просто по факту, вы же можете ещё и ресайзить потом окно, например чутка сресайзили и ещё раз потом чутка. Т.е. вы пишите ресайз и хотите чтобы вот при каждом ресайзе, максимум один раз сработала и только в самом конце, т.к. пользователь может реально вертеть этим окном полминуты, странная конечно ситуация, но тем не менее. Если так, то debounce с временем в секунды 2 и как только пользователь ничё не делает 2 секунды, то всё работаем. :))))
@@webelart спасибо, так и делаю)
Есть реализация throttle другая
Скините?
Почему у вас вместо 1,4,5 вызывается 1,3,5 ?
Напишите, какая минута.
@@webelart 21:06
3 вывелась, как последнее значение (по аналогии с последней 5), вопрос скорее в том - почему нет 4, т.е. вывод не 1 3 4 5?
@@TheDaZorg Слушайте правда странно, по логике должно 1,4,5 Но когда писала код, почему-то это не смутило. Вы пробовали набирать у себя в редакторе? Также выводится? Попробую найти время на выходных вновь набрать и проверить подробнее.
@@webelart С такими параметрами должно быть нагляднее:
const f = throttle(console.log, 400);
f(1);
f(2);
setTimeout(() => f(3), 100);
setTimeout(() => f(4), 500);
setTimeout(() => f(5), 900);
Вроде должно быть 1 3 4 5:
1 -выводится, т.к. это первое значение
2 - пропускаем, все понятно
3 - выводится, т.к. в f замкнуто значение ожидания в 400 и по окончании этого времени последнее значение будет 3 (такое поведение стало после рефакторинга для вывода последнего значения)
4 - по аналогии с 3
5 - по аналогии с 3
Если в f замкнуть время 500мс, то будет пограничный случай, т.к. время для 3 будет совпадать (но это не точно :D), у меня лично выводится тоже самое, что и в примере выше - 1 3 4 5, но я немного отрефакторил функцию:
function throttle(cb, delay) {
let isWaiting = false;
let savedArgs;
return (...args) => {
if (isWaiting) {
savedArgs = args;
return;
}
cb(...args);
isWaiting = true;
setTimeout(() => {
isWaiting = false;
if (savedArgs) {
cb(...savedArgs);
savedArgs = null;
}
}, delay);
};
}
Сложно было изучать JavaScript?)
🤔 Сложно так даже вспомнить, постепенно всё шло, усложнения, более сложные конструкции и их понимание. Помню самое сложное было начать вообще программировать, первый курс универа и c++, не сразу далось понимание механики.
В программировании очень важна практика, как только появились первые теоретические знания и сразу пробовать. Хотя чаще с JavaScript было наоборот, появилась задача и нужно понять как её сделать, да побыстрее. 😄
@@webelart у меня какие-то темы легко идут, а какие-то нет, сейчас засела на Switch Case, посмотрела видео и казалось всё очень просто, нахожу задания и всё....😅💔
@@user-er3le7uo6v 😊 Это нормально, у меня тоже таких тем полно, когда не сразу дошло.
Проще с date.now
Это когда в setTimout кладётся разница текущего и прошлого времени и сравнивается с delay?
Спасибо за видео!
А как правильно делать throttle, чтобы в итоге выполнялись все вызовы функций, просто не сразу, а с дилеем?
Я сделал такой вариант с использованием очереди, но, может, есть какой-то канонизированный вариант?
(Может, это вообще уже не throttle называется)
function throttle(callback, delay) {
let queueHead = {next: null, cb: null};
let queueTail = queueHead;
let isAwaiting = false;
return function(...args) {
if (isAwaiting) {
queueTail.cb = callback.bind(this, ...args);
queueTail.next = {next: null, cb: null};
queueTail = queueTail.next;
return;
}
callback(...args);
isThrottling = true;
moveQueue();
function moveQueue(){
setTimeout(() => {
if (!queueHead.cb) {
isAwaiting = false;
} else {
queueHead.cb();
queueHead = queueHead.next;
setTimeout(() => moveQueue(), delay);
}
}, delay);
};
}
}
let cat = {
meowLevel: '',
sayMeow: function(speechTone = 'calmly') {
console.log(`Meow!${this.meowLevel} - cat said ${speechTone}`);
this.meowLevel += '!';
},
}
let begForFood = throttle(cat.sayMeow.bind(cat), 500);
begForFood();
begForFood();
begForFood('impatiently');
begForFood('angrily');
begForFood('furiously');