【Продвинутый уровень 7-2】Устранение дребезга простыми словами

внешний интерфейс JavaScript

Обновление: Спасибо за вашу поддержку.Недавно официальный сайт блога был выкинут, чтобы каждый мог его систематически читать.В будущем будет больше контента и больше оптимизации.Нажмите здесь, чтобы просмотреть

------ Далее идет текст ------

введение

В предыдущем разделе мы узнали о функции дроссельной заслонки, ее определении, принципе реализации и реализации в подчеркивании. В этом разделе мы продолжим рассказывать об антидребезговой функции, описанной в предыдущем разделе. Структура такая же. В ней будут представлены определение, принцип реализации, два вида кодов реализации и, наконец, введение в реализацию с подчеркиванием. Добро пожаловать, чтобы сделать кирпич.

Если у вас есть какие-либо идеи или мнения, вы можете оставить сообщение в области комментариев.Следующее изображение представляет собой интеллект-карту этой статьи.Пожалуйста, смотрите мою интеллект-карту в высоком разрешении и другие статьи.Github.

111

Определение и интерпретация

Функция отката debounce относится к функции в течение определенного периода времени, независимо от того, сколько раз запускается обратный вызов,выполняются только в последний раз. Если мы установим функцию с временем ожидания 3 секунды, если в течение этих 3 секунд будет обнаружен запрос на вызов функции, таймер будет переустановлен на 3 секунды, пока в течение новых 3 секунд не будет запроса на вызов функции, затем функция в это время будет выполняться, иначе будет выводиться и т. д. Retime.

img

Возьмем небольшой пример: предположим, что при посадке на автобус водитель должен дождаться, пока последний человек войдет, прежде чем закрыть дверь.Каждый раз, когда входит новый человек, водитель очищает таймер и перезапускает таймер, подождите 1 минуту. до закрытия двери. Если в течение следующей минуты в автобус не войдет ни один пассажир, водитель подумает, что все пассажиры находятся в автобусе, закроет дверь и запустит автобус.

В настоящее время «пассажиры садятся в автобус» — это задача обратного вызова, которую мы часто обрабатываем событиями и продолжаем вливать; «1 минута» — это таймер, на основании которого водитель принимает решение «закрыть дверь». новый "пассажир" садится в автобус, он будет Клиром и переназначит время; "закрыть дверь" - последняя функция, которую нужно выполнить.

Если вы все еще не можете понять, посмотрите на картинку ниже, чтобы было понятнее, и нажмитеэта страницаПосмотрите визуальное сравнение троттлинга и стабилизации.其中 Regular 是不做任何处理的情况,throttle 是函数节流之后的结果(上一小节已介绍),debounce 是函数防抖之后的结果。

image-20190525193539745

Принцип и реализация

Принцип реализации заключается в использовании таймера, при первом выполнении функции устанавливается таймер, при последующем вызове функции обнаруживается, что таймер был установлен, а предыдущий таймер очищается, и новый таймер сбрасывается.Опустошенный таймер запускает выполнение функции, когда таймер истекает.

достичь 1

// 实现 1
// fn 是需要防抖处理的函数
// wait 是时间间隔
function debounce(fn, wait = 50) {
    // 通过闭包缓存一个定时器 id
    let timer = null
    // 将 debounce 处理结果当作函数返回
    // 触发事件回调时执行这个返回函数
    return function(...args) {
      	// 如果已经设定过定时器就清空上一次的定时器
        if (timer) clearTimeout(timer)
      
      	// 开始设定一个新的定时器,定时器结束后执行传入的函数 fn
        timer = setTimeout(() => {
            fn.apply(this, args)
        }, wait)
    }
}

// DEMO
// 执行 debounce 函数返回新函数
const betterFn = debounce(() => console.log('fn 防抖执行了'), 1000)
// 停止滑动 1 秒后执行函数 () => console.log('fn 防抖执行了')
document.addEventListener('scroll', betterFn)

достичь 2

Приведенная выше схема реализации может решить большинство сценариев использования, но если вы хотите реализовать fn при первом срабатывании события обратного вызова, этого недостаточно, а пока давайте перепишем функцию debounce и добавим функцию, которая выполняется сразу после первого триггера.

// 实现 2
// immediate 表示第一次是否立即执行
function debounce(fn, wait = 50, immediate) {
    let timer = null
    return function(...args) {
        if (timer) clearTimeout(timer)
      
      	// ------ 新增部分 start ------ 
      	// immediate 为 true 表示第一次触发后执行
      	// timer 为空表示首次触发
        if (immediate && !timer) {
            fn.apply(this, args)
        }
      	// ------ 新增部分 end ------ 
      	
        timer = setTimeout(() => {
            fn.apply(this, args)
        }, wait)
    }
}

// DEMO
// 执行 debounce 函数返回新函数
const betterFn = debounce(() => console.log('fn 防抖执行了'), 1000, true)
// 第一次触发 scroll 执行一次 fn,后续只有在停止滑动 1 秒后才执行函数 fn
document.addEventListener('scroll', betterFn)

Принцип реализации относительно прост.Необходимо судить, является ли входящее немедленное истинным.Кроме того, необходимо дополнительно судить, является ли это первым разом, чтобы выполнить функцию защиты от сотрясений.Судение по-прежнему заключается в том, является ли таймер пусто, покаimmediate && !timerВозвратите true, чтобы выполнить функцию fn, то естьfn.apply(this, args).

Улучшенный дроссель

Теперь рассмотрим ситуацию, если операция пользователя выполняется очень часто, следующая операция будет выполняться, не дожидаясь истечения установленного времени задержки, таймер будет часто сбрасываться и перегенерироваться, поэтому функция fn не может выполняться все время, в результате чего в отложенной операции пользователя нет ответа.

Есть идея объединить "троттлинг" и "анти-шейк" в расширенную версию функции троттлинга.Ключевым моментом является то, что "таймер может регенерироваться в течение времени ожидания, но пока время ожидания истекло, это должно быть Дать пользователю ответ». Эта комбинация идей может как раз решить проблемы, поднятые выше.

Прежде чем дать код «два в одном», давайте рассмотрим функцию газа, которая подробно описана в предыдущем разделе.

// 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)
    }
  }
}

В сочетании с кодами дроссельной заслонки и устранения дребезга расширенная функция дроссельной заслонки выглядит следующим образом: новая логика заключается в том, что когда разница во времени между текущим временем срабатывания и последним временем срабатывания меньше временного интервала, устанавливается новый таймер, который эквивалентно размещению кода устранения дребезга во временном интервале, меньшем временного интервала.

// fn 是需要节流处理的函数
// wait 是时间间隔
function throttle(fn, wait) {
  
  // previous 是上一次执行 fn 的时间
  // timer 是定时器
  let previous = 0, timer = null
  
  // 将 throttle 处理结果当作函数返回
  return function (...args) {
    
    // 获取当前时间,转换成时间戳,单位毫秒
    let now = +new Date()
    
    // ------ 新增部分 start ------ 
    // 判断上次触发的时间和本次触发的时间差是否小于时间间隔
    if (now - previous < wait) {
    	 // 如果小于,则为本次触发操作设立一个新的定时器
       // 定时器时间结束后执行函数 fn 
       if (timer) clearTimeout(timer)
       timer = setTimeout(() => {
          previous = now
        	fn.apply(this, args)
        }, wait)
    // ------ 新增部分 end ------ 
      
    } else {
       // 第一次执行
       // 或者时间间隔超出了设定的时间间隔,执行函数 fn
       previous = now
       fn.apply(this, args)
    }
  }
}

// DEMO
// 执行 throttle 函数返回新函数
const betterFn = throttle(() => console.log('fn 节流执行了'), 1000)
// 第一次触发 scroll 执行一次 fn,每隔 1 秒后执行一次函数 fn,停止滑动 1 秒后再执行函数 fn
document.addEventListener('scroll', betterFn)

Глядя на полный код, вы обнаружите, что эта идея очень похожа на идею реализации дроссельной заслонки в подчеркивании, представленную в предыдущей статье.

подчеркивание анализа исходного кода

После прочтения базовой версии приведенного выше кода я чувствую себя относительно расслабленным. Теперь давайте узнаем, как подчеркивание реализует функцию устранения дребезга, изучим несколько отличных идей и напрямую добавим код и комментарии. Этот анализ исходного кода зависит отunderscore 1.9.1реализация версии.

// 此处的三个参数上文都有解释
_.debounce = function(func, wait, immediate) {
  // timeout 表示定时器
  // result 表示 func 执行返回值
  var timeout, result;

  // 定时器计时结束后
  // 1、清空计时器,使之不影响下次连续事件的触发
  // 2、触发执行 func
  var later = function(context, args) {
    timeout = null;
    // if (args) 判断是为了过滤立即触发的
    // 关联在于 _.delay 和 restArguments
    if (args) result = func.apply(context, args);
  };

  // 将 debounce 处理结果当作函数返回
  var debounced = restArguments(function(args) {
    if (timeout) clearTimeout(timeout);
    if (immediate) {
      // 第一次触发后会设置 timeout,
      // 根据 timeout 是否为空可以判断是否是首次触发
      var callNow = !timeout;
      timeout = setTimeout(later, wait);
      if (callNow) result = func.apply(this, args);
    } else {
    	// 设置定时器
      timeout = _.delay(later, wait, this, args);
    }

    return result;
  });

  // 新增 手动取消
  debounced.cancel = function() {
    clearTimeout(timeout);
    timeout = null;
  };

  return debounced;
};

// 根据给定的毫秒 wait 延迟执行函数 func
_.delay = restArguments(function(func, wait, args) {
  return setTimeout(function() {
    return func.apply(null, args);
  }, wait);
});

По сравнению с базовой версией, описанной выше, подчеркивание имеет следующие функции.

  • 1. После завершения выполнения функции func вернуть значение результата
  • 2. По истечении времени таймера очистите тайм-аут, чтобы он не влиял на срабатывание следующего непрерывного события.
  • 3. Добавлена ​​функция ручной отмены отмены.
  • 4. После того, как немедленное значение равно true, оно будет выполняться только при первом запуске и не будет выполняться после завершения частого обратного вызова триггера.

резюме

  • Дросселирование функций и защита от сотрясения являются приложениями «замыканий» и «функций более высокого порядка».

  • Функция дросселирования газа относится к функции, которая выполняется один раз в течение определенного интервала времени (например, 3 секунды), в течение которого 3 секундыИгнорировать последующие запросы вызова функции

    • Под дросселированием можно понимать закручивание крана для выпуска воды при выращивании золотых рыбок, одна капля на 3 секунды
      • «Вода в трубе» — это задача обратного вызова, которую мы часто обрабатываем событиями и продолжаем вливать, она должна принять расположение «крана».
      • «Кран» — это дроссельный клапан, который регулирует расход воды и фильтрует недопустимые задачи обратного вызова.
      • "Drip" - выполнять функцию время от времени.
      • «3 секунды» - это интервал времени, который является основанием для «крана» для определения «капельки».
    • Применение: после отслеживания события прокрутки и добавления функции дроссельной заслонки выполняйте ее каждый фиксированный период времени.
    • План реализации 1: Используйте метку времени, чтобы определить, наступило ли время выполнения, запишите метку времени последнего выполнения, а затем выполните обратный вызов после каждого триггера, чтобы определить, достиг ли интервал между текущим временем и последним временем выполнения время разница (Xms), если да, выполнить и обновить метку времени последнего выполнения и т. д.
    • Реализация 2: Используйте таймер, например, когда событие прокрутки только что срабатывает, напечатайтеhello world, затем установите таймер на 1000 мс, а затем запускайте обратный вызов каждый раз, когда запускается событие прокрутки.Если таймер уже есть, обратный вызов не будет выполнять метод до тех пор, пока таймер не сработает и обработчик не будет очищен, а затем сбросьте таймер
  • Функция debounce debounce относится к функции в течение определенного периода времени, независимо от того, сколько раз запускается обратный вызов.выполняются только в последний раз

    • Anti-shake можно понимать как водитель ждет, пока последний человек войдет, а затем закрывает дверь.Каждый раз, когда входит новый человек, водитель сбрасывает таймер и перезапускает таймер.

      • «Пассажиры садятся в автобус» — это задача обратного вызова, которую мы часто обрабатываем событиями и продолжаем вливать.
      • «1 минута» — это таймер, на основании которого водитель принимает решение «закрыть дверь».Если в автобус садится новый «пассажир», он обнуляется и таймер запускается заново.
      • «Закрыть дверь» — последняя функция, которую необходимо выполнить
    • Применение: после добавления функции защиты от сотрясения к событию обратного вызова ввода, она будет запускаться только один раз после остановки ввода.

    • План реализации: использовать таймер, установить таймер при первом выполнении функции, а затем очистить предыдущий таймер при его вызове, когда будет обнаружено, что таймер был установлен, и сбросить новый таймер. , когда таймер истечет, вызовите функцию для выполнения

Ссылаться на

underscore.js

Принцип и практика оптимизации производительности интерфейса

Челнок статей