Обновление: Спасибо за вашу поддержку. Недавно я бросил сводку данных для всех, чтобы прочитать систему. В будущем будет больше контента и больше оптимизации.Нажмите здесь, чтобы просмотреть
------ Далее идет текст ------
введение
В предыдущем разделе мы подробно говорили о каррировании функций высшего порядка.Введя его определение и три применения каррирования, мы, наконец, реализовали общую функцию каррирования. В этом разделе мы продолжим говорить о регулировании функции в предыдущем разделе и дадим определение, принцип реализации и реализацию этой функции высокого порядка в подчеркивании Добро пожаловать, чтобы сделать кирпич.
Если у вас есть какие-либо идеи или мнения, вы можете оставить сообщение в области комментариев.Следующее изображение представляет собой интеллект-карту этой статьи.Пожалуйста, смотрите мою интеллект-карту в высоком разрешении и другие статьи.Github.
Определение и интерпретация
Дросселирование функции означает, что функция выполняется только один раз в определенный интервал времени (например, 3 секунды), и в течение этих 3 секундИгнорировать последующие запросы вызова функции, и не расширяет интервал. В первый раз, когда новый вызов функции встречается после окончания 3-секундного интервала, выполнение будет запущено, а затем последующие запросы вызова функции будут по-прежнему игнорироваться в течение новых 3 секунд и так далее.
Чтобы привести небольшой пример, я не знаю, выращивали ли вы когда-нибудь маленьких золотых рыбок или что-то в этом роде, когда вы были ребенком. Золотых рыбок нужно кормить водой. Когда вода впервые подключена, в трубе много воды. Когда вода наполнится наполовину, начните закручивать кран, чтобы уменьшить скорость потока воды. Сделайте каплю на 3 секунды, чтобы добавить кислород маленькой золотой рыбке, капая воду.
В настоящее время «вода в трубопроводе» — это задача обратного вызова, которую мы часто обрабатываем событиями и продолжаем вливать. вода и фильтрует недопустимые задачи обратного вызова; «Капельная вода» предназначена для выполнения функции через равные промежутки времени, а «3 секунды» — это интервал времени, который является основанием для «крана» для определения «капельной воды».
Если вы все еще не можете понять, посмотрите на картинку ниже, чтобы было понятнее, и нажмитеэта страницаПосмотрите визуальное сравнение троттлинга и стабилизации. Среди них Regular — это случай без какой-либо обработки, Throttle — результат после дросселирования функции, а debounce — результат после устранения дребезга функции (описано в следующем разделе).
Принцип и реализация
Регулирование функций очень подходит для сценариев, в которых функции вызываются часто, например, для событий window.onresize(), событий mousemove, хода загрузки и т. д. Использование API дроссельной заслонки очень просто, так как же нам реализовать функцию дроссельной заслонки?
Возможны два варианта реализации:
-
Первый заключается в использовании временной метки для определения того, наступило ли время выполнения, записи временной метки последнего выполнения, а затем выполнения обратного вызова каждый раз, когда происходит событие. и метка времени последнего выполнения достигла разницы во времени (Xms), если это так, выполните и обновите метку времени последнего выполнения и так далее.
-
Второй метод заключается в использовании таймера, например, когда событие прокрутки только что срабатывает, напечатайтеhello world, а затем установите таймер на 1000 мс. После этого каждый раз, когда срабатывает событие прокрутки, будет срабатывать обратный вызов. Если таймер уже есть, обратный вызов не будет выполнять метод, пока не сработает таймер и обработчик не будет очищен , а затем сбросьте таймер.
Здесь мы используем первую схему для реализации, сохраняем предыдущую переменную через замыкание и оцениваем разницу во времени между текущим временем и предыдущим временем каждый раз, когда срабатывает функция дросселя.Если разница во времени меньше времени ожидания, то игнорировать этот триггер события. Если оно больше, чем время ожидания, установите предыдущее время на текущее и выполните функцию fn.
Давайте реализуем это шаг за шагом, сначала реализовав замыкание для сохранения предыдущей переменной.
const throttle = (fn, wait) => {
// 上一次执行该函数的时间
let previous = 0
return function(...args) {
console.log(previous)
...
}
}
После выполнения функции дросселя будет возвращена новая функция, которую мы назовем betterFn.
const betterFn = function(...args) {
console.log(previous)
...
}
Предыдущее значение переменной можно получить или изменить в функции betterFn, и betterFn будет выполняться, когда отслеживается обратный вызов или инициируется событие, т. е.betterFn(), поэтому в этой новой функции можно оценить разницу во времени между текущим временем и предыдущим временем.
const betterFn = function(...args) {
let now = +new Date();
if (now - previous > wait) {
previous = now
// 执行 fn 函数
fn.apply(this, args)
}
}
Функция регулирования реализуется путем объединения двух приведенных выше фрагментов кода, поэтому полная реализация выглядит следующим образом.
// fn 是需要执行的函数
// wait 是时间间隔
const throttle = (fn, wait = 50) => {
// 上一次执行 fn 的时间
let previous = 0
// 将 throttle 处理结果当作函数返回
return function(...args) {
// 获取当前时间,转换成时间戳,单位毫秒
let now = +new Date()
// 将当前时间和上一次执行函数的时间进行对比
// 大于等待时间就把 previous 设置为当前时间并执行函数 fn
if (now - previous > wait) {
previous = now
fn.apply(this, args)
}
}
}
// DEMO
// 执行 throttle 函数返回新函数
const betterFn = throttle(() => console.log('fn 函数执行了'), 1000)
// 每 10 毫秒执行一次 betterFn 函数,但是只有时间差大于 1000 时才会执行 fn
setInterval(betterFn, 10)
Подчеркивание интерпретации исходного кода
Приведенный выше код реализует простую функцию дросселирования, но подчеркивание реализует более сложные функции, то есть добавляются две новые функции.
- Настройте, следует ли отвечать на обратный вызов в начале события (начальный параметр, игнорируется, если значение равно false)
- Настройте, следует ли отвечать на обратный вызов после завершения события (завершающий параметр, игнорируемый при ложном значении)
При настройке {leading: false} обратный вызов в начале события не выполняется, при настройке { trailing: false} обратный вызов после завершения события не выполняется, но следует отметить, что два не могут быть настраивается одновременно.
Таким образом, есть 3 способа вызвать функцию дросселя в подчеркивании: по умолчанию (с головкой и хвостом), настройка {leading: false} и настройка {trailing: false}. Как упоминалось выше, есть две схемы реализации дросселя, одна — судить по отметке времени, а другая — управлять созданием и уничтожением таймера.
Существует проблема с первым решением для реализации этих трех методов вызова, то есть на обратный вызов нельзя ответить, когда событие перестанет запускаться, поэтому { trailing: true } не может вступить в силу.
Со вторым решением тоже есть проблема: из-за того, что таймер задерживается, на обратный вызов нужно ответить, когда событие перестанет срабатывать, поэтому { trailing: false } не может вступить в силу.
Решение, принятое подчеркиванием, состоит в том, чтобы использовать два решения вместе для достижения этой функции.
const throttle = function(func, wait, options) {
var timeout, context, args, result;
// 上一次执行回调的时间戳
var previous = 0;
// 无传入参数时,初始化 options 为空对象
if (!options) options = {};
var later = function() {
// 当设置 { leading: false } 时
// 每次触发回调函数后设置 previous 为 0
// 不然为当前时间
previous = options.leading === false ? 0 : _.now();
// 防止内存泄漏,置为 null 便于后面根据 !timeout 设置新的 timeout
timeout = null;
// 执行函数
result = func.apply(context, args);
if (!timeout) context = args = null;
};
// 每次触发事件回调都执行这个函数
// 函数内判断是否执行 func
// func 才是我们业务层代码想要执行的函数
var throttled = function() {
// 记录当前时间
var now = _.now();
// 第一次执行时(此时 previous 为 0,之后为上一次时间戳)
// 并且设置了 { leading: false }(表示第一次回调不执行)
// 此时设置 previous 为当前值,表示刚执行过,本次就不执行了
if (!previous && options.leading === false) previous = now;
// 距离下次触发 func 还需要等待的时间
var remaining = wait - (now - previous);
context = this;
args = arguments;
// 要么是到了间隔时间了,随即触发方法(remaining <= 0)
// 要么是没有传入 {leading: false},且第一次触发回调,即立即触发
// 此时 previous 为 0,wait - (now - previous) 也满足 <= 0
// 之后便会把 previous 值迅速置为 now
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
// clearTimeout(timeout) 并不会把 timeout 设为 null
// 手动设置,便于后续判断
timeout = null;
}
// 设置 previous 为当前时间
previous = now;
// 执行 func 函数
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
// 最后一次需要触发的情况
// 如果已经存在一个定时器,则不会进入该 if 分支
// 如果 {trailing: false},即最后一次不需要触发了,也不会进入这个分支
// 间隔 remaining milliseconds 后触发 later 方法
timeout = setTimeout(later, remaining);
}
return result;
};
// 手动取消
throttled.cancel = function() {
clearTimeout(timeout);
previous = 0;
timeout = context = args = null;
};
// 执行 _.throttle 返回 throttled 函数
return throttled;
};
резюме
-
Дросселирование функции означает, что функция выполняется только один раз в определенный интервал времени (например, 3 секунды), и в течение этих 3 секундИгнорировать последующие запросы вызова функции
-
Под дросселированием можно понимать закручивание крана для выпуска воды при выращивании золотых рыбок, одна капля на 3 секунды
- «Вода в трубе» — это задача обратного вызова, которую мы часто обрабатываем событиями и продолжаем вливать, она должна принять расположение «крана».
- «Кран» — это дроссельный клапан, который регулирует расход воды и фильтрует недопустимые задачи обратного вызова.
- "Drip" - выполнять функцию время от времени.
- «3 секунды» - это интервал времени, который является основанием для «крана» для определения «капельки».
-
Существует 2 вида схем реализации троттлинга
-
Первый заключается в использовании временной метки для определения того, наступило ли время выполнения, записи временной метки последнего выполнения, а затем выполнения обратного вызова каждый раз, когда происходит событие. и метка времени последнего выполнения достигла разницы во времени (Xms), если это так, выполните и обновите метку времени последнего выполнения и так далее.
-
Второй метод заключается в использовании таймера, например, когда событие прокрутки только что срабатывает, напечатайтеhello world, а затем установите таймер на 1000 мс. После этого каждый раз, когда срабатывает событие прокрутки, будет срабатывать обратный вызов. Если таймер уже есть, обратный вызов не будет выполнять метод, пока не сработает таймер и обработчик не будет очищен , а затем сбросьте таймер.
-
Ссылаться на
Принцип и практика оптимизации производительности интерфейса
Челнок статей
- [Дополнительно 6-2] Углубленное каррирование приложений функций высшего порядка
- [Дополнительно 6-1] Анализ функций высшего порядка в JavaScript
- [Расширенная фаза 5-3] Углубленное изучение проблемы яйца функции и объекта
- [Расширенная фаза 5-2] Цепочка графических прототипов и ее преимущества и недостатки наследования
- [Расширенная фаза 5-1] Повторное распознавание конструкторов, прототипов и цепочек прототипов