Понимание функций Debounce в Underscore

внешний интерфейс JavaScript Ajax Underscore.js

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

Функция Debounce — это функция, которая может ограничивать частоту срабатывания указанной функции. Мы можем понять это какнепрерывный вызовОдна и та же функция выполняется несколько раз, и только один раз получается результат выполнения функции, но при повторном вызове через какой-то промежуток времени могут быть снова получены новые результаты, а конкретный период времени зависит от наших настроек. Каковы сценарии применения этой функции?

Например, мы пишем функцию прослушивания событий DOM,

window.onscroll = function(){
    console.log('Got it!');
}

Теперь, когда мы двигаем колесико мыши, мы видим, что событие запущено. Но мы можем обнаружить, что когда мы прокручиваем колесо мыши, наша консоль постоянно печатает сообщения, потому что событие прокрутки окна постоянно вызывается нами.

В текущем сценарии это может быть безобидным поведением, но можно предвидеть, что когда наша функция прослушивания событий (обработчик событий) включает некоторые сложные операции (такие как запросы Ajax, рендеринг DOM, вычисление больших объемов данных), она будет влияет ли это на производительность компьютера; в некоторых старых моделях или младших версиях браузеров (особенно IE) может вызывать сбои. Итак, в настоящее время мы должны найти способ выполнить только определенное количество обработчиков событий в течение определенного периода времени.

Понимание функций устранения отказов

Я рассказал о некоторых концепциях и сценариях применения, но это все еще слишком много.Что такое функция устранения дребезга?

Мы можем понять это на следующих примерах:

Предположим, у вас есть следующий код:

//自己实现的简单演示代码,未实现immediate功能,欢迎改进。
var debounce = function (callback, delay, immediate) {
	var timeout, result;
	return function () {
		var callNow;
		if (timeout)
			clearTimeout(timeout);
		callNow = !timeout && immediate;
		if (callNow) {
			result = callback.apply(this, Array.prototype.slice.call(arguments, 0));
			timeout = {};
		}
		else {
			timeout = setTimeout(() => {
				callback.apply(this, Array.prototype.slice.call(arguments, 0));
			}, delay);
		}
	};
};
var s = debounce(() => {
	console.log('yes...');
}, 2000);
window.onscroll = s;

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

Действуйте следующим образом:

  • Скопируйте приведенный выше код, откройте браузер, откройте консоль (F12), затем вставьте код и нажмите Enter для выполнения.
  • Непрерывно прокручивайте мышь, чтобы увидеть, есть ли какие-либо выходные данные в консоли.
  • Остановите прокрутку мыши и снова прокрутите мышь в течение 2 секунд, чтобы увидеть, есть ли какие-либо выходные данные.
  • После непрерывной прокрутки остановитесь более чем на 2 с, чтобы увидеть, есть ли выход.

Выполняя описанные выше шаги, мы можем обнаружить, что при непрерывной прокрутке мыши сообщение на консоли не печатается, а когда мы останавливаемся в течение 2 с и снова прокручиваем, сообщение не выводится; но когда мы останавливаемся более чем на 2 с, мы можно увидеть управление Станция имеет вывод сообщений.

Это функция устранения дребезга. В последовательных триггерах (независимо от продолжительности) можно получить только один триггерный эффект. Запускайте непрерывно в течение указанного периода времени, и вы можете получить эффект запуска не более одного раза.

Реализация подчеркивания

Исходный код подчеркивания выглядит следующим образом (с комментариями кода):

// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
//去抖函数,传入的函数在wait时间之后(或之前)执行,并且只会被执行一次。
//如果immediate传递为true,那么在函数被传递时就立即调用。
//实现原理:涉及到异步JavaScript,多次调用_.debounce返回的函数,会一次性执行完,但是每次调用
//该函数又会清空上一次的TimeoutID,所以实际上只执行了最后一个setTimeout的内容。
_.debounce = function (func, wait, immediate) {
	var timeout, result;

	var later = function (context, args) {
		timeout = null;
		//如果没有传递args参数,那么func不执行。
		if (args) result = func.apply(context, args);
	};

	//被返回的函数,该函数只会被调用一次。
	var debounced = restArgs(function (args) {
		//这行代码的作用是清除上一次的TimeoutID,
		//使得如果有多次调用该函数的场景时,只执行最后一次调用的延时。
		if (timeout) clearTimeout(timeout);
		if (immediate) {
			////如果传递了immediate并且timeout为空,那么就立即调用func,否则不立即调用。
			var callNow = !timeout;
			//下面这行代码,later函数内部的func函数注定不会被执行,因为没有给later传递参数。
			//它的作用是确保返回了一个timeout,并且保持到wait毫秒之后,才执行later,
			//清空timeout。而清空timeout是在immediate为true时,callNow为true的条件。
			//timeout = setTimeout(later, wait)的存在是既保证上升沿触发,
			//又保证wait内最多触发一次的必要条件。
			timeout = setTimeout(later, wait);
			if (callNow) result = func.apply(this, args);
		} else {
			//如果没有传递immediate,那么就使用_.delay函数延时执行later。
			timeout = _.delay(later, wait, this, args);
		}

		return result;
	});

	//该函数用于取消当前去抖效果。
	debounced.cancel = function () {
		clearTimeout(timeout);
		timeout = null;
	};

	return debounced;
};

Вы можете видеть, что подчеркивание использует метод закрытия для определения двух частных свойств: тайм-аут и результат, а также два частных метода позже и debounced. В конечном итоге вернет debounced как функцию после обработки. timeout используется для принятия и сохранения TimeoutID, возвращаемого setTimeout, result используется для выполнения результата выполнения функции func, переданной пользователем, а более поздний метод используется для выполнения переданной функции func.

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

Используя механизм асинхронного выполнения JavaScript, JavaScript сначала выполнит весь синхронный код, а затем перейдет в очередь событий для выполнения всех асинхронных задач.

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

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

Суммировать

  • Debounce — это способ ограничить частоту выполнения функции.
  • Функция устранения дребезга может быть запущена не более одного раза в течение указанного времени, а функция устранения дребезга может быть запущена только один раз.
  • Реализация подчеркивания debounce основана на механизме асинхронного выполнения JavaScript, который сначала выполняет синхронный код, а затем выполняет асинхронный код в очереди событий.

Ссылаться на