함수 조절이란 간단히 말해서 짧은 시간 간격 내에 함수를 계속해서 호출할 수 없다는 의미입니다. 마지막 함수 실행 이후 지정한 시간 간격이 경과한 경우에만 다음 함수 호출이 가능합니다.
기능 조절의 원리는 꽤 간단하다고 다들 생각해 보셨을 텐데요, 바로 타이머입니다. 시간을 트리거할 때 먼저 setTimeout을 사용하여 이벤트를 잠시 지연시킨 다음 실행합니다. 이 시간 간격 내에 이벤트가 다시 트리거되면 원래 타이머를 지우고 잠시 동안 실행을 지연하기 위해 새 타이머를 setTimeout합니다. .그게 다야.
다음 시나리오는 빈번한 이벤트로 인해 발생하는 경우가 많으므로 DOM 작업, 리소스 로딩 및 기타 과도한 활동이 자주 수행되어 UI가 일시 중지되거나 브라우저가 충돌할 수도 있습니다.
1. 창 개체의 크기 조정 및 스크롤 이벤트
2. 슈팅 게임에서 마우스 이동 이벤트
3. mousedown, keydown 이벤트
4. 텍스트 입력, 자동 완료 키업 이벤트
실제로 창 크기 조정 이벤트의 경우 실제 요구 사항은 대부분 크기 n 변경을 중지하는 것입니다. 밀리초 후 후속 처리를 수행합니다. 반면 대부분의 다른 이벤트는 특정 빈도로 후속 처리를 수행해야 합니다. 이러한 두 가지 요구에 부응하여 디바운스와 스로틀이라는 두 가지 솔루션이 등장했습니다.
스로틀과 디바운스는 요청과 응답 속도 불일치 문제를 해결하는 두 가지 솔루션입니다. 둘 사이의 차이점은 서로 다른 전략을 선택하는 데 있습니다.
throttle 동일한 시간 간격으로 기능을 실행합니다.
debounce 시간 간격 t 내에 이벤트가 다시 발생하면 시간이 다시 시작되고 중지 시간이 t 이상이 될 때까지 함수가 실행되지 않습니다.
1. 스로틀 기능 간단 구현
function throttle(fn, threshhold, scope) { threshhold || (threshhold = 250); var last, timer; return function () { var context = scope || this; var now = +new Date(), args = arguments; if (last && now - last + threshhold < 0) { // hold on to it clearTimeout(deferTimer); timer = setTimeout(function () { last = now; fn.apply(context, args); }, threshhold); } else { last = now; fn.apply(context, args); } };}
콜 방식
$('body').on('mousemove', throttle(function (event) { console.log('tick'); }, 1000));
디바운스 기능 간단 구현
function debounce(fn, delay) { var timer = null; return function () { var context = this, args = arguments; clearTimeout(timer); timer = setTimeout(function () { fn.apply(context, args); }, delay); };}
호출 방법
$('input.username').keypress(debounce(function (event) { // do the Ajax request }, 250));
3. 간단한 캡슐화 구현
/** * throttle * @param fn, wait, debounce */var throttle = function ( fn, wait, debounce ) { var timer = null, // 定时器 t_last = null, // 上次设置的时间 context, // 上下文 args, // 参数 diff; // 时间差 return funciton () { var curr = + new Date(); var context = this, args = arguments; clearTimeout( timer ); if ( debounce ) { // 如果是debounce timer = setTimeout( function () { fn.apply( context, args ); }, wait ); } else { // 如果是throttle if ( !t_last ) t_last = curr; if ( curr - t_last >= wait ) { fn.apply( context, wait ); context = wait = null; } } }}/** * debounce * @param fn, wait */var debounce = function ( fn, wait ) { return throttle( fn, wait, true ); }
요약: 이 두 가지 방법은 mousemove, keydown, keyup , 키 누르기, 스크롤 등
네이티브 이벤트를 제어하지 않고 바인딩만 하면 브라우저가 정지되고 사용자 경험이 저하됩니다. js 성능을 향상시키기 위해서는 위와 유사한 이벤트를 사용할 때 함수 조절이나 함수 디바운스를 사용하여 제어하는 것이 좋습니다.
4. Underscore v1.7.0 관련 소스코드 분석
1. _.throttle 기능
_.throttle = function(func, wait, options) { var context, args, result; var timeout = null; // 定时器 var previous = 0; // 上次触发的时间 if (!options) options = {}; var later = function() { previous = options.leading === false ? 0 : _.now(); timeout = null; result = func.apply(context, args); if (!timeout) context = args = null; }; return function() { var now = _.now(); // 第一次是否执行 if (!previous && options.leading === false) previous = now; // 这里引入了一个remaining的概念:还剩多长时间执行事件 var remaining = wait - (now - previous); context = this; args = arguments; // remaining <= 0 考虑到事件停止后重新触发或者 // 正好相差wait的时候,这些情况下,会立即触发事件 // remaining > wait 没有考虑到相应场景 // 因为now-previous永远都是正值,且不为0,那么 // remaining就会一直比wait小,没有大于wait的情况 // 估计是保险起见吧,这种情况也是立即执行 if (remaining <= 0 || remaining > wait) { if (timeout) { clearTimeout(timeout); timeout = null; } previous = now; result = func.apply(context, args); if (!timeout) context = args = null; // 是否跟踪 } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; };};
에서 알 수 있듯이 위에서 밑줄은 더 일반적인 상황을 고려합니다: options.leading:
처음 실행할지 여부 기본값은 true입니다. 이는 처음으로 실행된다는 의미입니다. {leading:false를 전달합니다. } 옵션의 첫 번째 실행을 비활성화합니다. trailing: 마지막으로 실행할지 여부, 기본값은 true입니다. 이는 마지막으로 실행된다는 의미입니다. {trailing: false}를 전달하면 마지막으로 실행되지 않는다는 의미입니다. 최초 실행 여부는 이벤트가 처음 트리거될 때 먼저 이벤트를 트리거할지 여부를 의미합니다. 필요한 경우 이전=0이고 나머지가 음수 값이면 함수가 즉시 호출됩니다. 소위 마지막 실행 여부는 이벤트가 끝난 후 이 메서드가 마지막으로 실행되는 시간입니다. 실행하려면 타이머를 설정합니다. 즉, 이벤트가 끝난 후 실행됩니다. remianing > wait는 클라이언트 시간이 수정되었음을 나타냅니다.
2._.debounce 함수
_.debounce = function(func, wait, immediate) { // immediate默认为false var timeout, args, context, timestamp, result; var later = function() { // 当wait指定的时间间隔期间多次调用_.debounce返回的函数,则会不断更新timestamp的值,导致last < wait && last >= 0一直为true,从而不断启动新的计时器延时执行func var last = _.now() - timestamp; if (last < wait && last >= 0) { timeout = setTimeout(later, wait - last); } else { timeout = null; if (!immediate) { result = func.apply(context, args); if (!timeout) context = args = null; } } }; return function() { context = this; args = arguments; timestamp = _.now(); // 第一次调用该方法时,且immediate为true,则调用func函数 var callNow = immediate && !timeout; // 在wait指定的时间间隔内首次调用该方法,则启动计时器定时调用func函数 if (!timeout) timeout = setTimeout(later, wait); if (callNow) { result = func.apply(context, args); context = args = null; } return result; };};
_.debounce의 좋은 점은 func 함수 호출의 지연을 cleanTimeout을 호출하여 조정하는 대신 재귀적으로 시작한다는 것입니다. 구현하다.
위 내용은 에디터가 JavaScript 성능 최적화를 위해 소개한 Throttling 및 Debounce 기능입니다. 궁금한 점이 있으면 에디터에게 메시지를 남겨주세요. 제 시간에 모든 사람에게 답장을 보내드립니다. 또한 PHP 중국어 웹사이트를 지원해 주신 모든 분들께 감사드립니다!
자바스크립트 성능 최적화 기능 조절(throttle)과 기능 디바운스(debounce)에 관련된 더 많은 글은 PHP 중국어 홈페이지를 주목해주세요!