1. Для чего нужна функция защиты от тряски
Есть следующий код
window.onresize = () => {
console.log('触发窗口监听回调函数')
}
Когда мы масштабируем окно браузера на нашем ПК, событие может легко запускаться 30 раз в секунду. То же самое верно, когда мобильный телефон инициирует другие обратные вызовы мониторинга времени Dom.
Функция обратного вызова здесь просто печатает строку.Если функция обратного вызова более сложна, вполне возможно, что нагрузка на браузер будет очень высокой, и взаимодействие с пользователем будет очень плохим.
resize
илиscroll
Обратный вызов прослушивателя для событий Dom будет срабатывать часто, поэтому нам нужно ограничить его.
Во-вторых, реализация идеи
Проще говоря, функция debounce — это выполнение непрерывного вызова функции в течение определенного периода времени только один раз.Первоначальные идеи реализации таковы:
При первом вызове функции создается таймер для запуска кода через заданный интервал. Когда функция вызывается во второй раз, она очищает предыдущий таймер и устанавливает новый. Если предыдущий таймер уже отработал, эта операция не имеет смысла. Однако, если предыдущий таймер не был выполнен, он фактически заменяется новым таймером. Намерение состоит в том, чтобы выполнить функцию только после того, как запрос на выполнение функции остановился на некоторое время.
3. Сценарии отказа от приложений
- Каждый раз, когда изменение размера/прокрутки запускает событие статистики
- Проверка ввода текста (отправьте запрос AJAX для проверки после непрерывного ввода текста, просто подтвердите один раз)
В-четвертых, финальная версия функции анти-встряски
Код говорит, если есть ошибка, укажите
function debounce(method, wait, immediate) {
let timeout
// debounced函数为返回值
// 使用Async/Await处理异步,如果函数异步执行,等待setTimeout执行完,拿到原函数返回值后将其返回
// args为返回函数调用时传入的参数,传给method
let debounced = function(...args) {
return new Promise (resolve => {
// 用于记录原函数执行结果
let result
// 将method执行时this的指向设为debounce返回的函数被调用时的this指向
let context = this
// 如果存在定时器则将其清除
if (timeout) {
clearTimeout(timeout)
}
// 立即执行需要两个条件,一是immediate为true,二是timeout未被赋值或被置为null
if (immediate) {
// 如果定时器不存在,则立即执行,并设置一个定时器,wait毫秒后将定时器置为null
// 这样确保立即执行后wait毫秒内不会被再次触发
let callNow = !timeout
timeout = setTimeout(() => {
timeout = null
}, wait)
// 如果满足上述两个条件,则立即执行并记录其执行结果
if (callNow) {
result = method.apply(context, args)
resolve(result)
}
} else {
// 如果immediate为false,则等待函数执行并记录其执行结果
// 并将Promise状态置为fullfilled,以使函数继续执行
timeout = setTimeout(() => {
// args是一个数组,所以使用fn.apply
// 也可写作method.call(context, ...args)
result = method.apply(context, args)
resolve(result)
}, wait)
}
})
}
// 在返回的debounced函数上添加取消方法
debounced.cancel = function() {
clearTimeout(timeout)
timeout = null
}
return debounced
}
Следует отметить, что если требуется возвращаемое значение исходной функции, внешняя функция функции, которая вызывает функцию защиты от сотрясений, должна использовать синтаксис Async/Await для ожидания возврата результата выполнения.
См. код для использования:
function square(num) {
return Math.pow(num, 2)
}
let debouncedFn = debounce(square, 1000, false)
window.addEventListener('resize', async () => {
let val
try {
val = await debouncedFn(4)
} catch (err) {
console.error(err)
}
// 停止缩放1S后输出:
// 原函数的返回值为:16
console.log(`原函数返回值为${val}`)
}, false)
Конкретные этапы реализации см. ниже.
5. Реализация DEBOUNCE
1. Реализация в расширенном программировании на JavaScript (третье издание)
function debounce(method, context) {
clearTimeout(method.tId)
method.tId = setTimeout(() => {
method.call(context)
}, 1000)
}
function print() {
console.log('Hello World')
}
window.onresize = debounce(print)
Продолжаем масштабировать окно, и когда оно остановится на 1S, распечатаем Hello World.
Есть где оптимизировать: Этот метод реализации имеет побочный эффект (Side Effect), изменяет входное значение (метод) и добавляет атрибуты к методу.
2. Оптимизировать первую версию: устранить побочные эффекты и изолировать таймеры.
function debounce(method, wait, context) {
let timeout
return function() {
if (timeout) {
clearTimeout(timeout)
}
timeout = setTimeout(() => {
method.call(context)
}, wait)
}
}
3. Оптимизируйте вторую версию: автоматически настройте ее так, чтобы она указывала на правильный
Перед функцией мы должны вручную передаваться в контекст выполнения функцийcontext
, оптимизация теперь указывает это на правильный объект.
function debounce(method, wait) {
let timeout
return function() {
// 将method执行时this的指向设为debounce返回的函数被调用时的this指向
let context = this
if (timeout) {
clearTimeout(timeout)
}
timeout = setTimeout(() => {
method.call(context)
}, wait)
}
}
4. Оптимизировать третью версию: функция может передавать параметры
Даже если нашей функции не нужно передавать параметры, не забывайте, что JavaScript предоставит событие объекта события в обработчике события, поэтому нам нужно реализовать функцию передачи параметров.
function debounce(method, wait) {
let timeout
// args为返回函数调用时传入的参数,传给method
return function(...args) {
let context = this
if (timeout) {
clearTimeout(timeout)
}
timeout = setTimeout(() => {
// args是一个数组,所以使用fn.apply
// 也可写作method.call(context, ...args)
method.apply(context, args)
}, wait)
}
}
5. Оптимизировать четвертую версию: предоставить возможность немедленного исполнения
Иногда я не хочу ждать, пока событие перестанет срабатывать, я хочу немедленно выполнить функцию, а затем подождать, пока n миллисекунд после того, как она перестанет срабатывать, прежде чем перезапускать выполнение.
function debounce(method, wait, immediate) {
let timeout
return function(...args) {
let context = this
if (timeout) {
clearTimeout(timeout)
}
// 立即执行需要两个条件,一是immediate为true,二是timeout未被赋值或被置为null
if (immediate) {
// 如果定时器不存在,则立即执行,并设置一个定时器,wait毫秒后将定时器置为null
// 这样确保立即执行后wait毫秒内不会被再次触发
let callNow = !timeout
timeout = setTimeout(() => {
timeout = null
}, wait)
if (callNow) {
method.apply(context, args)
}
} else {
// 如果immediate为false,则函数wait毫秒后执行
timeout = setTimeout(() => {
// args是一个类数组对象,所以使用fn.apply
// 也可写作method.call(context, ...args)
method.apply(context, args)
}, wait)
}
}
}
6. Оптимизируйте пятое издание: предоставьте функцию отмены
Иногда нам нужно иметь возможность вручную отменить анти-встряску в течение периода без срабатывания.Код реализован следующим образом:
function debounce(method, wait, immediate) {
let timeout
// 将返回的匿名函数赋值给debounced,以便在其上添加取消方法
let debounced = function(...args) {
let context = this
if (timeout) {
clearTimeout(timeout)
}
if (immediate) {
let callNow = !timeout
timeout = setTimeout(() => {
timeout = null
}, wait)
if (callNow) {
method.apply(context, args)
}
} else {
timeout = setTimeout(() => {
method.apply(context, args)
}, wait)
}
}
// 加入取消功能,使用方法如下
// let myFn = debounce(otherFn)
// myFn.cancel()
debounced.cancel = function() {
clearTimeout(timeout)
timeout = null
}
}
На данный момент мы полностью реализовали функцию устранения дребезга в подчеркивании.
6. Оставшиеся вопросы
Функция, которой нужен антишейк, может иметь возвращаемое значение, нам нужно разобраться с этой ситуацией,underscore
Метод обработки заключается в возврате возвращаемого функцией значения в возвращаемомdebounced
Функция возвращается снова, но это на самом деле проблема. если параметрimmediate
Входящее значение неtrue
Если функция после антитряски срабатывает в первый раз, если исходная функция имеет возвращаемое значение, возвращаемое значение фактически не может быть получено, поскольку исходная функция находится в состоянииsetTimeout
Внутри это асинхронная задержка, иreturn
Синхронное выполнение, поэтому возвращаемое значениеundefined
.
Возвращаемое значение, полученное вторым триггером, фактически является возвращаемым значением первого выполнения, возвращаемое значение, полученное третьим триггером, фактически является возвращаемым значением второго выполнения и так далее.
1. Используйте функцию обратного вызова для обработки возвращаемого значения функции
function debounce(method, wait, immediate, callback) {
let timeout, result
let debounced = function(...args) {
let context = this
if (timeout) {
clearTimeout(timeout)
}
if (immediate) {
let callNow = !timeout
timeout = setTimeout(() => {
timeout = null
}, wait)
if (callNow) {
result = method.apply(context, args)
// 使用回调函数处理函数返回值
callback && callback(result)
}
} else {
timeout = setTimeout(() => {
result = method.apply(context, args)
// 使用回调函数处理函数返回值
callback && callback(result)
}, wait)
}
}
debounced.cancel = function() {
clearTimeout(timeout)
timeout = null
}
return debounced
}
Таким образом, мы можем передать функцию обратного вызова для обработки возвращаемого значения функции, когда функция отклоняется.Код выглядит следующим образом:
function square(num) {
return Math.pow(num, 2)
}
let debouncedFn = debounce(square, 1000, false, val => {
console.log(`原函数的返回值为:${val}`)
})
window.addEventListener('resize', () => {
debouncedFn(4)
}, false)
// 停止缩放1S后输出:
// 原函数的返回值为:16
2. Используйте промисы для обработки возвращаемых значений
function debounce(method, wait, immediate) {
let timeout, result
let debounced = function(...args) {
// 返回一个Promise,以便可以使用then或者Async/Await语法拿到原函数返回值
return new Promise(resolve => {
let context = this
if (timeout) {
clearTimeout(timeout)
}
if (immediate) {
let callNow = !timeout
timeout = setTimeout(() => {
timeout = null
}, wait)
if (callNow) {
result = method.apply(context, args)
// 将原函数的返回值传给resolve
resolve(result)
}
} else {
timeout = setTimeout(() => {
result = method.apply(context, args)
// 将原函数的返回值传给resolve
resolve(result)
}, wait)
}
})
}
debounced.cancel = function() {
clearTimeout(timeout)
timeout = null
}
return debounced
}
Как использовать один: При вызове функции защиты от сотрясений используйтеthen
Получить возвращаемое значение исходной функции
function square(num) {
return Math.pow(num, 2)
}
let debouncedFn = debounce(square, 1000, false)
window.addEventListener('resize', () => {
debouncedFn(4).then(val => {
console.log(`原函数的返回值为:${val}`)
})
}, false)
// 停止缩放1S后输出:
// 原函数的返回值为:16
Используйте метод два: внешняя функция, вызывающая функцию антивибрации, использует синтаксис Async/Await для ожидания возврата результата выполнения.
См. код для использования:
function square(num) {
return Math.pow(num, 2)
}
let debouncedFn = debounce(square, 1000, false)
window.addEventListener('resize', async () => {
let val
try {
val = await debouncedFn(4)
} catch (err) {
console.error(err)
}
console.log(`原函数返回值为${val}`)
}, false)
// 停止缩放1S后输出:
// 原函数的返回值为:16
7. Справочные статьи
Тема JavaScript: изучите защиту от сотрясений с помощью подчеркивания
Реализация функции подчеркивания debounce
Если есть какие-либо ошибки или неточности, пожалуйста, поправьте меня, большое спасибо.