인기 있는 프론트엔드 면접 질문 중 하나입니다. 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} />
이 접근 방식은 괜찮아 보이지만 몇 가지 문제가 있습니다.
이러한 문제의 해결책은 디바운싱입니다.
기본 아이디어는 사용자가 입력을 멈출 때까지 기다리는 것입니다. 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)} />
디바운싱을 활용하도록 기존 코드를 확장했습니다.
디바운스 함수는 두 개의 인수를 취하는 일반 유틸리티 함수입니다.
함수 내부에서는 실제 함수(fn) 호출을 지연시키기 위해 setTimeout을 사용합니다. 타이머가 끝나기 전에 fn을 다시 호출하면 타이머가 재설정됩니다.
업데이트된 구현을 사용하면 사용자가 15자를 입력하더라도 API 호출은 1번만 수행됩니다(각 키를 누르는 데 걸리는 시간이 500밀리초 미만이라고 가정). 이로써 이 기능 구축을 시작할 때 겪었던 모든 문제가 해결되었습니다.
프로덕션 코드베이스에서는 디바운스 유틸리티 함수를 직접 코딩할 필요가 없습니다. 귀하의 회사에서는 이미 이러한 메소드가 포함된 lodash와 같은 JS 유틸리티 라이브러리를 사용하고 있을 가능성이 있습니다.
디바운싱은 성능 측면에서는 훌륭하지만 변경 사항에 대한 알림을 받기 전에 x초를 기다리기를 원하지 않는 몇 가지 시나리오가 있습니다.
Google Docs나 Figma와 같은 공동 작업 공간을 구축하고 있다고 상상해 보세요. 주요 기능 중 하나는 사용자가 다른 사용자의 변경 사항을 실시간으로 알 수 있다는 것입니다.
지금까지 우리는 두 가지 접근 방식만 알고 있습니다.
여기서 조절이 필요합니다. 위에서 언급한 두 가지 접근 방식의 중간에 있습니다. 기본 아이디어는 주기적 간격으로 알림을 보내는 것입니다. 키를 누를 때마다 알림을 보내는 것이 아니라 주기적으로 알림을 보내는 것입니다.
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)} />
스로틀 기능을 활용하도록 기존 코드를 수정했습니다. 두 가지 인수가 필요합니다:
구현은 간단합니다. 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는 스로틀 유틸리티 기능도 제공합니다.
프런트엔드캠프
로다시
위 내용은 디바운싱 및 제한의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!