предисловие
В последнее время я видел различные фейсбуки, и мне кажется, что троттлинг против сотрясений никогда не отсутствовал. Хотя я тоже использовал их в проекте, я всегда был
На этот раз мы должны расставить их четко.
Отказаться
концепция
Буквально это означает предотвращение дрожания. В программе есть предотвращение повторного выполнения кода (функции) в течение определенного времени.
Выполнять после запуска функции в течение n секунд и пересчитывать, если другая функция выполняется в течение n секунд.
🌰
Существует поле ввода, пользователь вводит имя пользователя, а затем запрашивает интерфейс у бэкэнда, чтобы узнать, существует ли имя пользователя.
Общая практика заключается в том, чтобы попросить интерфейс оценить, когда поле ввода не в фокусе. Тем не менее, это нехорошо.Если это результат обратной связи в режиме реального времени с контентом, введенным пользователем, это полезно для пользовательского опыта.
Когда пользователь вводит контент, код результата обратной связи в реальном времени выглядит следующим образом:
// 输入框
<input type="text" id="input"/>
let ipt = document.getElementById('input');
// 用户输入内容实时反馈结果
ipt.addEventListener('input',function(){
let val = this.value;
handleSendPhone(val);
});
// 请求接口
function handleSendPhone(val){
ajaxRequest({
user:val
}).then(res => {
console.log(res)
})
}
//模拟数据
let items = ['abc','aaa','bbb','ccc','ddd'];
//模拟ajax请求
function ajaxRequest({user}){
return new Promise((resolved,rejected) => {
setTimeout(() => {
let res = items.includes(user)?'正确':'错误';
resolved(res);
},500)
});
}
Левая сторона приведенного выше gif — это содержимое пользовательского ввода, а правая — интерфейс запроса. Очевидно, что неуместно запрашивать интерфейс, когда пользователь вводит контент, что приведет к пустой трате ресурсов.
Состояние защиты от сотрясений — это когда функция выполняется часто. Это как раз соответствует нашим вышеперечисленным требованиям: частые запросы к интерфейсу.
Теперь настала очередь антишока.
Реализовать идеи
При заданном временном интервале выполнения функции n, если ни одна функция не будет выполнена снова в течение n секунд, функция будет выполнена. Если функция снова выполняется в течение n секунд, пересчитайте время выполнения функции.
Первое издание:
Используйте setTimeout, чтобы отложить выполнение входящей функции.До прибытия отложенного выполнения, если функция выполняется снова, очистите таймер и дайте setTimeout переустановить время. Таким образом, условием выполнения функции является то, что переданная функция не будет выполняться снова до истечения setTimeout, а переданная функция будет выполняться в это время.
/**
* @fn : 要执行的函数
* @delay : 执行执行函数的时间间隔
*/
function debounce(fn,delay){
let timer; // 定时器
return function(...args){ // 形成闭包
timer&&clearTimeout(timer); // 当函数再次执行时,清除定时器,让定时器重新开始计时
// 利用定时器,让指定函数延迟执行。
timer = setTimeout(function(){
// 执行传入的指定函数
fn();
},delay)
}
}
Приведенная выше реализация функции защиты от сотрясений не реализует передачу параметров и эту привязку.
второе издание:
/**
* @fn : 要执行的函数
* @delay : 执行执行函数的时间间隔
*/
function debounce(fn,delay){
let timer; // 定时器
return function(...args){ // 形成闭包 外部执行的函数其实是这个return出去的函数。
// args 为函数调用时传的参数。
let context = this; // this 为函数执行时的this绑定。
timer&&clearTimeout(timer); // 当函数再次执行时,清除定时器,让定时器重新开始计时
// 利用定时器,让指定函数延迟执行。
timer = setTimeout(function(){
// 执行传入的指定函数,利用apply更改this绑定和传参
fn.apply(context,args);
},delay)
}
}
использовать
// 输入框
<input type="text" id="input"/>
let ipt = document.getElementById('input');
let handler = debounce(handleSendPhone,500);
//handler:debounce执行后return的函数。
ipt.addEventListener('input',function(){
let val = this.value;
handler(val);
});
// 请求接口
function handleSendPhone(val){
ajaxRequest({
user:val
}).then(res => {
console.log(res)
})
}
Эффект контраста
В приведенном выше сравнении добавлен код после защиты от встряхивания, когда контент постоянно вводится, запрос не будет выполняться непрерывно, ноПоследний ввод и следующий ввод будут выполнены через определенный интервал временипросить. Таким образом, сравнение очевидно: не затрагивая бизнес-требований, анти-встряска позволяет избежать пустой траты ресурсов.
Немедленное исполнение издание
На приведенной выше диаграмме мы можем видеть стабилизацию изображения gif, непрерывный ввод содержимого, когда пользователь не возвращает результаты, но выполняется только тогда, когда интервал нашего выполнения.
Если есть такой эффект, если есть продукт с тонким (дьявольским) сердцем (сверление), он не должен хотеть этого, он захочет запросить интерфейс, чтобы он сразу же отреагировал на результат, когда пользователь вводит, а затем подождет, пока входной интервал соответствует нашему интервалу времени.
Идеи:
Затем нам нужно, чтобы описанная выше функция защиты от сотрясений выполнялась один раз при ее вызове. По сравнению с анти-тряской только что, немедленная версия анти-тряски — это всего лишь еще один шаг, который нужно выполнить немедленно.
Код:
/**
* @fn : 要执行的函数
* @delay : 执行执行函数的时间间隔
* @immediate : 是否立即执行函数 true 表立即执行,false 表非立即执行
*/
function debounce(fn,delay,immediate){
let timer; // 定时器
return function(...args){ // 形成闭包 外部执行的函数其实是这个return出去的函数。
// args 为函数调用时传的参数。
let context = this; // this 为函数执行时的this绑定。
timer&&clearTimeout(timer); // 当函数再次执行时,清除定时器,让定时器重新开始计时
// immediate为true 表示第一次触发就执行
if(immediate){
// 执行一次之后赋值为false
immediate = false;
fn.apply(context, args)
}
// 利用定时器,让指定函数延迟执行。
timer = setTimeout(function(){
// immediate 赋值为true 下次输入时 还是会立即执行
immediate = true;
// 执行传入的指定函数,利用apply更改传入函数内部的this绑定,传入 args参数
fn.apply(context,args);
},delay)
}
}
Контрастный эффект:
На самом деле анти-шейк очень прост для понимания. Например, когда мы поднимаемся на лифте, когда кто-то входит в лифт, дверь лифта открывается, а затем начинается обратный отсчет, чтобы закрыть дверь. При постоянном потоке людей, входящих в лифт, лифт не закроет двери по истечении отведенного времени. Вместо этого, когда входит один человек, лифт начинает отсчитывать время закрытия двери, а когда входит другой человек, обратный отсчет сбрасывается. Когда никто не войдет в лифт до окончания обратного отсчета, дверь закроется. Это то, что мы называем анти-встряской. Непрерывные люди здесь — это часто запускаемые функции, а закрывающаяся дверь — это последняя функция, которую мы хотим выполнить. Время закрытия обратного отсчета — это интервал, с которым мы хотим выполнить функцию.
Дроссель
концепция
Когда функция срабатывает непрерывно, она будет срабатывать только один раз в течение указанного единичного времени.
🌰
К вам приходил продакт-менеджер... : Меня пока не устраивает эффект, буду менять. Я: Как вы говорите, это должно быть изменено на (сердце:!@#$%^&%). Менеджер по продукту: Эффект только что, если пользователь постоянно вводил данные, он должен дождаться ввода, чтобы получить результат. Теперь он изменен, чтобы запрашивать интерфейс каждый раз, когда пользователь вводит несколько символов.
Как упоминалось выше, дросселирование означает, что когда функция запускается непрерывно, она будет запускаться один раз в течение заданного интервала времени. Когда пользователь входит непрерывно, интерфейс запрашивается каждые n секунд. Замаскированно это эквивалентно запросу интерфейса каждый раз, когда пользователь вводит несколько символов.
Реализовать идеи
Если функция продолжает срабатывать, пусть функция задерживает выполнение, если функция все еще срабатывает во время отложенного выполнения, это не имеет никакого эффекта. До окончания выполнения функции задержки может быть выполнено следующее выполнение функции задержки.
Базовая версия
/**
* @fn : 要执行的函数
* @delay : 每次执行函数的时间间隔
*/
function throttle(fn,delay){
let timer; // 定时器
return function(...args){
let context = this;
// 如果timer存在,说明函数还未该执行 也就是距离上次函数执行未间隔指定的时间
if(timer) return;
// 如果函数执行之后还有函数还在触发,再延迟执行。
timer = setTimeout(function(){
// 当函数执行时,让timer为null。
timer = null;
fn.apply(context,args);
},delay);
}
}
использовать
<input type="text" id="input"/>
let ipt = document.getElementById('input');
let handler = throttle(handleSendPhone,1000);
ipt.addEventListener('input',function(){
let val = this.value;
handler(val);
});
// 请求接口
function handleSendPhone(val){
ajaxRequest({
user:val
}).then(res => {
console.log(`请求结果为:${res}`)
})
}
Контрастный эффект:
Проблема с базовой версией
У приведенной выше базовой версии троттлинга есть еще большая проблема, например, в последний раз она не будет выполняться, а в первый раз всегда будет выводить первый бит строки.
Эффект следующий:
В соответствии со значениями, введенными на рисунке, мы должны ожидать, что на выходе будетaaaa, aaaaaa.
Обновленная версия
идеи
Если текущее время минус время последнего выполнения функции больше заданного временного интервала, можно считать, что текущая функция может выполняться.
который:currTime - prevTime > delay.
/**
* @fn : 要执行的函数
* @delay : 每次执行函数的时间间隔
*/
function throttle(fn,delay){
let timer;
let prevTime; // 记录上一次执行的时间
return function(...args){
let currTime = Date.now(); // 获取当前时间时间戳
let context = this;
if(!prevTime) prevTime = currTime; // 第一次执行时prevTime赋值为当前时间
timer&&clearTimeout(timer); // 每次都清除定时器,保证定时器只是在最后一次执行
if(currTime - prevTime > delay){ // 如果为true ,则表示两次执行函数的时间间隔为delay.
prevTime = currTime;
fn.apply(context,args);
clearTimeout(timer); // 清除定时器。用来处理假如函数停止调用时刚好函数也停止执行,不需要获取后续的值。 详见下面定时器的介绍。
return;
}
// 当上面执行currTime - prevTime > delay 为false时,执行定时器。
// 用来处理:假如下次函数执行时间未到,函数不继续调用了,会造成最后一次函数执行 到 最后一次函数调用之间的值获取不到。
timer = setTimeout(function(){
prevTime = Date.now();
timer = null;
fn.apply(context,args);
},delay);
}
}
Эффект обновленной версии
Суммировать
- Anti-shake и throttling в определенной степени похожи, оба предназначены для экономии ресурсов. Улучшить пользовательский опыт.
- Anti-shake заключается в задержке выполнения на указанное время при срабатывании функции.Если функция снова сработает во время задержки, время выполнения будет пересчитано.
- Дросселирование заключается в том, что когда функция запускается непрерывно, она будет выполняться только один раз в течение указанного времени.
- Грубо говоря, это количество контроля тряски и частота контроля троттлинга.
заявление
-
Стабилизатор
Запросите интерфейс в соответствии с содержимым поля ввода и верните результат. Если интерфейс запрашивается в режиме реального времени, это приведет к пустой трате ресурсов. Необходимо предположить, что ввод пользователя завершен, а затем перейти к интерфейсу запроса, например, установить временной интервал в 1 с, когда пользователь не вошел после интервала в 1 с, считается, что ввод завершен, и когда символ вводится до прихода 1 с, то интервал снова равен 1 с.
-
дросселирование
При вычислении расстояния от элемента до верха окна в реальном времени на странице нужно следить за изменениями полосы прокрутки, вычислять координаты элемента, а затем обрабатывать некоторую логику. При отслеживании изменений полосы прокрутки не обязательно вычислять позицию элемента в реальном времени, а использовать дросселирование, например вычисление раз в 300 мс, что позволяет избежать частого выполнения некоторых кодов. также можно избежать
offsetHeight, вызывая чрезмерную перекраску.
наконец
Если в тексте есть ошибки, пожалуйста, не забудьте оставить сообщение для исправления, большое спасибо.
Ставьте лайки, давайте учиться вместе и добиваться прогресса вместе.