디바운싱 및 제한

王林
풀어 주다: 2024-08-09 20:32:30
원래의
438명이 탐색했습니다.

Debouncing and Throttling

인기 있는 프론트엔드 면접 질문 중 하나입니다. JS, 성능 및 FE 시스템 설계에 대한 인터뷰 대상자의 지식을 테스트합니다.

프런트엔드 인터뷰 질문 시리즈의 질문 #2입니다. 준비 수준을 높이고 전반적인 최신 정보를 얻으려면 FrontendCamp에 가입하는 것을 고려해 보세요.


디바운싱과 제한은 동일한 원리(지연)로 작동하지만 여전히 접근 방식과 사용 사례가 매우 다릅니다.

두 개념 모두 성능이 뛰어난 애플리케이션을 개발하는 데 유용합니다. 매일 방문하는 거의 모든 웹사이트는 어떤 방식으로든 디바운싱과 스로틀링을 사용합니다.

디바운싱

잘 알려진 디바운싱 사용 사례는 자동 완성(또는 자동 완성)입니다.

수천 개의 제품이 포함된 전자상거래 웹사이트에 대한 검색 기능을 구축한다고 가정해 보겠습니다. 사용자가 무언가를 검색하려고 하면 앱은 사용자의 쿼리 문자열과 일치하는 모든 제품을 가져오기 위해 API 호출을 수행합니다.

const handleKeyDown = async (e) => {
 const { value } = e.target;
 const result = await search(value);
 // set the result to a state and then render on UI
}

<Input onKeyDown={handleKeyDown} />
로그인 후 복사

이 접근 방식은 괜찮아 보이지만 몇 가지 문제가 있습니다.

  1. 각 키 누름 이벤트에 대해 API 호출을 수행하고 있습니다. 사용자가 15자를 입력하면 단일 사용자에 대한 15개의 API 호출이 됩니다. 이는 절대로 확장되지 않습니다.
  2. 15개 API 호출의 결과가 도착하면 마지막 하나만 필요합니다. 이전 14번의 호출 결과는 삭제됩니다. 이는 사용자의 대역폭을 많이 소모하며 느린 네트워크의 사용자에게는 상당한 지연이 발생합니다.
  3. UI에서 이러한 15개의 API 호출은 다시 렌더링을 트리거합니다. 구성 요소가 지연됩니다.

이러한 문제의 해결책은 디바운싱입니다.

기본 아이디어는 사용자가 입력을 멈출 때까지 기다리는 것입니다. API 호출을 지연하겠습니다.

const debounce = (fn, delay) => {
 let timerId;
 return function(...args) {
  const context = this;

  if (timerId) {
    clearTimeout(timerId);
  };
  timerId = setTimeout(() => fn.call(context, ...args), delay);
 }
}

const handleKeyDown = async (e) => {
 const { value } = e.target;
 const result = await search(value);
 // set the result to a state and then render on UI
}

<Input onKeyDown={debounce(handleKeyDown, 500)} />
로그인 후 복사

디바운싱을 활용하도록 기존 코드를 확장했습니다.

디바운스 함수는 두 개의 인수를 취하는 일반 유틸리티 함수입니다.

  1. fn: 지연될 것으로 예상되는 함수 호출입니다.
  2. 지연: 밀리초 단위의 지연입니다.

함수 내부에서는 실제 함수(fn) 호출을 지연시키기 위해 setTimeout을 사용합니다. 타이머가 끝나기 전에 fn을 다시 호출하면 타이머가 재설정됩니다.

업데이트된 구현을 사용하면 사용자가 15자를 입력하더라도 API 호출은 1번만 수행됩니다(각 키를 누르는 데 걸리는 시간이 500밀리초 미만이라고 가정). 이로써 이 기능 구축을 시작할 때 겪었던 모든 문제가 해결되었습니다.

프로덕션 코드베이스에서는 디바운스 유틸리티 함수를 직접 코딩할 필요가 없습니다. 귀하의 회사에서는 이미 이러한 메소드가 포함된 lodash와 같은 JS 유틸리티 라이브러리를 사용하고 있을 가능성이 있습니다.

조절

디바운싱은 성능 측면에서는 훌륭하지만 변경 사항에 대한 알림을 받기 전에 x초를 기다리기를 원하지 않는 몇 가지 시나리오가 있습니다.

Google Docs나 Figma와 같은 공동 작업 공간을 구축하고 있다고 상상해 보세요. 주요 기능 중 하나는 사용자가 다른 사용자의 변경 사항을 실시간으로 알 수 있다는 것입니다.

지금까지 우리는 두 가지 접근 방식만 알고 있습니다.

  1. Noob 접근 방식: 사용자가 마우스 포인터를 움직이거나 무언가를 입력할 때마다 API를 호출합니다. 상황이 얼마나 악화될 수 있는지 이미 알고 계십니다.
  2. 디바운싱 접근 방식: 성능 측면을 해결하지만 UX 관점에서는 끔찍합니다. 동료가 300단어 문단을 작성하면 결국 한 번만 알림을 받을 수 있습니다. 아직도 실시간으로 간주되나요?

여기서 조절이 필요합니다. 위에서 언급한 두 가지 접근 방식의 중간에 있습니다. 기본 아이디어는 주기적 간격으로 알림을 보내는 것입니다. 키를 누를 때마다 알림을 보내는 것이 아니라 주기적으로 알림을 보내는 것입니다.

const throttle = (fn, time) => {
 let lastCalledAt = 0;

 return function(...args) {
  const context = this;
  const now = Date.now();
  const remainingTime = time - (now - lastCalledAt);

  if (remainingTime <= 0) {
   fn.call(context, ...args);
   lastCalledAt = now;
  }
 }
}

const handleKeyDown = async (e) => {
 const { value } = e.target;
 // save it DB and also notify other peers
 await save(value);
}

<Editor onKeyDown={throttle(handleKeyDown, 1000)} />
로그인 후 복사

스로틀 기능을 활용하도록 기존 코드를 수정했습니다. 두 가지 인수가 필요합니다:

  1. fn: 스로틀을 수행하는 실제 기능입니다.
  2. 시간: 함수 실행이 허용되는 간격입니다.

구현은 간단합니다. lastCalledAt에 함수가 마지막으로 호출된 시간을 저장합니다. 다음번에 함수 호출이 오면 시간이 지났는지 확인하고 나서야 fn을 실행합니다.

거의 다 왔지만 이 구현에는 버그가 있습니다. 일부 데이터가 포함된 마지막 함수 호출이 해당 시간 간격 내에 이루어지고 그 이후에는 호출이 이루어지지 않으면 어떻게 될까요? 현재 구현에서는 일부 데이터가 손실됩니다.

이 문제를 해결하기 위해 인수를 다른 변수에 저장하고 나중에 이벤트가 수신되지 않으면 호출되도록 제한 시간을 시작합니다.

const throttle = (fn, time) => {
 let lastCalledAt = 0;
 let lastArgs = null;
 let timeoutId = null;

 return function(...args) {
  const context = this;
  const now = Date.now();
  const remainingTime = time - (now - lastCalledAt);

  if (remainingTime <= 0) {
   // call immediately
   fn.call(context, ...args);
   lastCalledAt = now;
   if (timeoutId) {
     clearTimeout(timeoutId);
     timeoutId = null;
   }
  } else {
    // call later if no event is received
    lastArgs = args;
    if (!timeoutId) {
      timeoutId = setTimeout(() => {
        fn.call(context, ...lastArgs);
        lastCalledAt = Date.now();
        lastArgs = null;
        timeoutId = null;
      }, remainingTime);
    }
  }
 }
}
로그인 후 복사

이 업데이트된 구현을 통해 어떤 데이터도 놓치지 않습니다.

Lodash는 스로틀 유틸리티 기능도 제공합니다.


요약

  1. 디바운싱과 조절은 성능 최적화 기술입니다.
  2. 두 가지 모두 비슷한 원리로 작동합니다. 즉, 작업을 지연시킵니다.
  3. Debounce는 마지막 이벤트가 수신된 후 t를 기다리는 반면 Throttling은 t 시간에 주기적으로 fn을 실행합니다.
  4. 디바운싱은 검색 기능에서 사용되며, 스로틀링은 실시간 앱에서 사용됩니다(이에 국한되지 않음).

자원

프런트엔드캠프
로다시

위 내용은 디바운싱 및 제한의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:dev.to
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!