자바스크립트의 단순 스로틀
자바스크립트에서 간단한 스로틀을 찾고 있습니다.lodash나 언더스코어와 같은 라이브러리들이 그것을 가지고 있다는 것을 알고 있지만, 오직 하나의 기능에 대해서만 그러한 라이브러리들을 포함하는 것은 과도한 일이 될 것입니다.
jQuery와 비슷한 기능이 있는지도 확인하고 있었는데 찾을 수 없었습니다.
작동하는 스로틀을 발견했고, 여기 코드가 있습니다.
function throttle(fn, threshhold, scope) {
threshhold || (threshhold = 250);
var last,
deferTimer;
return function () {
var context = scope || this;
var now = +new Date,
args = arguments;
if (last && now < last + threshhold) {
// hold on to it
clearTimeout(deferTimer);
deferTimer = setTimeout(function () {
last = now;
fn.apply(context, args);
}, threshhold);
} else {
last = now;
fn.apply(context, args);
}
};
}
이것의 문제점은 스로틀 시간이 완료된 후에 한 번 더 기능을 실행한다는 것입니다.따라서 키 누름 시 10초마다 발생하는 스로틀을 만들었다고 가정해 보겠습니다. 키 누름을 2번 수행해도 10초가 완료된 후에도 두 번째 키 누름이 발생합니다.저는 이런 행동을 원하지 않습니다.
underscore.js 또는 lodash 소스 코드를 사용하여 이 함수의 테스트가 잘 된 버전을 찾습니다.
언더스코어.js 자체에 대한 모든 참조를 제거하기 위해 언더스코어 코드를 약간 수정한 버전은 다음과 같습니다.
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time. Normally, the throttled function will run
// as much as it can, without ever going more than once per `wait` duration;
// but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto.
function throttle(func, wait, options) {
var context, args, result;
var timeout = null;
var previous = 0;
if (!options) options = {};
var later = function() {
previous = options.leading === false ? 0 : Date.now();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
return function() {
var now = Date.now();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
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;
};
};
지원을 강조하는 모든 옵션이 필요하지 않다면 이 코드를 단순화할 수 있습니다.
아래에서 이 기능의 매우 단순하고 구성할 수 없는 버전을 찾으십시오.
function throttle (callback, limit) {
var waiting = false; // Initially, we're not waiting
return function () { // We return a throttled function
if (!waiting) { // If we're not waiting
callback.apply(this, arguments); // Execute users function
waiting = true; // Prevent future invocations
setTimeout(function () { // After a period of time
waiting = false; // And allow future invocations
}, limit);
}
}
}
편집 1: 밑줄에 대한 또 다른 참조, thx에서 @Zettam의 코멘트를 제거했습니다.
편집 2: lodash 및 코드 단순화 가능성에 대한 제안 추가, @lolzery @wowzery의 코멘트에 thx
편집 3: 인기 있는 요청으로 인해 @vsync의 코멘트를 차용하여 매우 단순하고 구성할 수 없는 버전의 함수를 추가했습니다.
이거는?
function throttle(func, timeFrame) {
var lastTime = 0;
return function () {
var now = Date.now();
if (now - lastTime >= timeFrame) {
func();
lastTime = now;
}
};
}
간단하죠.
출처를 확인하는 것에 관심이 있을 것입니다.
callback: 호출해야 하는 함수를 사용합니다.
limit: 제한 시간 내에 함수를 호출해야 하는 횟수
time: 제한 카운트를 재설정하기 위한 시간 범위
기능 및 용도:사용자가 1분에 10번 호출할 수 있는 API가 있다고 가정합니다.
function throttling(callback, limit, time) {
/// monitor the count
var calledCount = 0;
/// refresh the `calledCount` varialbe after the `time` has been passed
setInterval(function(){ calledCount = 0 }, time);
/// creating a closure that will be called
return function(){
/// checking the limit (if limit is exceeded then do not call the passed function
if (limit > calledCount) {
/// increase the count
calledCount++;
callback(); /// call the function
}
else console.log('not calling because the limit has exceeded');
};
}
////////////////////////////////////////////////////////////
// how to use
/// creating a function to pass in the throttling function
function cb(){
console.log("called");
}
/// calling the closure function in every 100 milliseconds
setInterval(throttling(cb, 3, 1000), 100);
방문자들을 ) 논의를 , 의 에( 을 를 가 을 의 가 하지 을 의 )throttle
lodash
번들을 의지는을는것는이다면지은다이는ysoe'es,더r,oean은teerd의throttle
의 묶음 안에lodash
예를 들어 ES6의 경우 다음과 같습니다.
import throttle from 'lodash/throttle';
그리고 또 하나는.throttle
지의 온리 lodash
간단한 것과 함께 사용할 수 있는 calling.import
ES6는require
ES서에서
윈도우 크기 조절 이벤트를 위해 스로틀/디바운스 기능이 필요했는데 궁금해서 이것들이 무엇인지, 어떻게 작동하는지도 알고 싶었습니다.
SO에 대한 여러 블로그 게시물과 QA를 읽어 보았지만, 이들은 모두 이를 지나치게 복잡하게 하거나 라이브러리를 제안하거나 간단한 JS 구현이 아닌 설명만 제공하는 것 같습니다.
내용이 풍부해서 설명을 드리지 않겠습니다.구현 방법은 다음과 같습니다.
function throttle(callback, delay) {
var timeoutHandler = null;
return function () {
if (timeoutHandler == null) {
timeoutHandler = setTimeout(function () {
callback();
timeoutHandler = null;
}, delay);
}
}
}
function debounce(callback, delay) {
var timeoutHandler = null;
return function () {
clearTimeout(timeoutHandler);
timeoutHandler = setTimeout(function () {
callback();
}, delay);
}
}
조정이 필요할 수 있습니다(예: 처음에는 콜백이 즉시 호출되지 않음).
동작의 차이를 확인합니다(창 크기 조정 시도).
function throttle(callback, delay) {
var timeoutHandler = null;
return function () {
if (timeoutHandler == null) {
timeoutHandler = setTimeout(function () {
callback();
timeoutHandler = null;
}, delay);
}
}
}
function debounce(callback, delay) {
var timeoutHandler = null;
return function () {
clearTimeout(timeoutHandler);
timeoutHandler = setTimeout(function () {
callback();
}, delay);
}
}
var cellDefault = document.querySelector("#cellDefault div");
var cellThrottle = document.querySelector("#cellThrottle div");
var cellDebounce = document.querySelector("#cellDebounce div");
window.addEventListener("resize", function () {
var span = document.createElement("span");
span.innerText = window.innerWidth;
cellDefault.appendChild(span);
cellDefault.scrollTop = cellDefault.scrollHeight;
});
window.addEventListener("resize", throttle(function () {
var span = document.createElement("span");
span.innerText = window.innerWidth;
cellThrottle.appendChild(span);
cellThrottle.scrollTop = cellThrottle.scrollHeight;
}, 500));
window.addEventListener("resize", debounce(function () {
var span = document.createElement("span");
span.innerText = window.innerWidth;
cellDebounce.appendChild(span);
cellDebounce.scrollTop = cellDebounce.scrollHeight;
}, 500));
table {
border-collapse: collapse;
margin: 10px;
}
table td {
border: 1px solid silver;
padding: 5px;
}
table tr:last-child td div {
width: 60px;
height: 200px;
overflow: auto;
}
table tr:last-child td span {
display: block;
}
<table>
<tr>
<td>default</td>
<td>throttle</td>
<td>debounce</td>
</tr>
<tr>
<td id="cellDefault">
<div></div>
</td>
<td id="cellThrottle">
<div></div>
</td>
<td id="cellDebounce">
<div></div>
</td>
</tr>
</table>
9LOC에서 ES6에 스로틀 기능을 구현한 방법은 다음과 같습니다. 도움이 되기를 바랍니다.
function throttle(func, delay) {
let timeout = null
return function(...args) {
if (!timeout) {
timeout = setTimeout(() => {
func.call(this, ...args)
timeout = null
}, delay)
}
}
}
이 링크를 클릭하면 작동 방식을 확인할 수 있습니다.
저는 여기서 "js의 단순한 스로틀"이라고 하기에는 너무 복잡한 답변을 많이 봤습니다.
대부분의 단순한 답변은 실행을 다음 간격으로 미루는 대신 "조정 중"인 호출을 무시합니다.
다음은 "In throttle" 호출도 처리하는 간단한 구현 방법입니다.
const throttle = (func, limit) => {
let lastFunc;
let lastRan = Date.now() - (limit + 1); //enforces a negative value on first run
return function(...args) {
const context = this;
clearTimeout(lastFunc);
lastFunc = setTimeout(() => {
func.apply(context, args);
lastRan = Date.now();
}, limit - (Date.now() - lastRan)); //negative values execute immediately
}
}
이는 단순한 디바운스의 경우와 거의 동일한 구현입니다.함수가 마지막으로 실행된 시점을 추적해야 하는 타임아웃 지연에 대한 계산을 추가할 뿐입니다.아래 참조:
const debounce = (func, limit) => {
let lastFunc;
return function(...args) {
const context = this;
clearTimeout(lastFunc);
lastFunc = setTimeout(() => {
func.apply(context, args)
}, limit); //no calc here, just use limit
}
}
ES6의 간단한 솔루션.코데펜 데모
const handleOnClick = () => {
console.log("hello")
}
const throttle = (func, delay) => {
let timeout = null;
return function (...args) {
if (timeout === null) {
func.apply(this, args);
timeout = setTimeout(() => {
timeout = null;
}, delay)
}
}
}
document.querySelector("#button").addEventListener("click", throttle(handleOnClick, 500))
<button type="button" id="button">Click me</button>
다음은 제 자신의 비카스 게시물입니다.
throttle: function (callback, limit, time) {
var calledCount = 0;
var timeout = null;
return function () {
if (limit > calledCount) {
calledCount++;
callback();
}
if (!timeout) {
timeout = setTimeout(function () {
calledCount = 0
timeout = null;
}, time);
}
};
}
저는 그것을 사용하는 것을 발견합니다.setInterval
좋은 생각이 아닙니다.
선행 및 후행 호출의 경우:
const throttle = (fn, ms) => {
let locked = false
return function () {
if (!locked) {
locked = true
fn.apply(this, arguments)
setTimeout(() => {
fn.apply(this, arguments)
locked = false
}, ms)
}
}
}
테스트 케이스:
function log({ gender, address }) {
console.log({
name: this.name,
gender,
address,
})
}
const jack = {
name: 'Jack',
log: throttle(log, 3000),
}
Array.from({ length: 5 }, () => jack.log({ gender: 'Male', address: 'LA' }))
조절 기능이 있는 npm 패키지를 만들었습니다.
npm 설치 기능-스캐플러
스로틀 앤 큐
최대 W 밀리초마다 호출할 수 있는 함수 버전을 반환합니다. 여기서 W는 대기합니다.W보다 더 자주 발생하는 펑에 대한 호출은 매 Wms 호출되도록 대기열에 들어갑니다.
조절된 업데이트
최대 W 밀리초마다 호출할 수 있는 함수 버전을 반환합니다. 여기서 W는 대기합니다.W보다 더 자주 발생하는 호출의 경우 마지막 호출이 호출됩니다(마지막으로 우선).
목을 조르다
는 최대 W 밀리초마다 함수가 호출되도록 제한합니다. 여기서 W는 대기합니다.W를 통한 호출이 삭제됨
이 목적에 맞는 도서관이 있는데, 엠버의 Backburner.js입니다.
https://github.com/BackburnerJS/
그렇게 쓰실 거예요.
var backburner = new Backburner(["task"]); //You need a name for your tasks
function saySomething(words) {
backburner.throttle("task", console.log.bind(console, words)
}, 1000);
}
function mainTask() {
"This will be said with a throttle of 1 second per word!".split(' ').map(saySomething);
}
backburner.run(mainTask)
이 스로틀 기능은 ES6를 기반으로 합니다.콜백 함수는 인수(args)를 사용하지만, 여전히 스로틀 함수와 함께 작동합니다.앱의 필요에 따라 지연 시간을 자유롭게 사용자 정의하십시오. 개발 모드에는 100ms당 1회가 사용되며, 이벤트 "on input"은 사용 빈도가 높은 예에 불과합니다.
const callback = (...args) => {
console.count('callback throttled with arguments:', args);
};
throttle = (callback, limit) => {
let timeoutHandler = 'null'
return (...args) => {
if (timeoutHandler === 'null') {
timeoutHandler = setTimeout(() => {
callback(...args)
timeoutHandler = 'null'
}, limit)
}
}
}
window.addEventListener('oninput', throttle(callback, 100));
추신: @Anshul이 설명한 바와 같이, 조절은 시간이 지남에 따라 함수를 호출할 수 있는 최대 횟수를 강제합니다."100밀리초마다 최대 한 번 이 함수를 실행합니다."에서와 같이.
아래 예제에서 단추를 여러 번 클릭해 보십시오.myFunc
기능은 3초에 한번만 실행됩니다.함수를throttle
실행할 함수 및 지연과 함께 전달됩니다.폐쇄를 반환합니다. 폐쇄는 다음에 저장됩니다.obj.throttleFunc
. 그 이후로obj.throttleFunc
폐쇄, 값 저장isRunning
그 안에 유지됩니다.
function throttle(func, delay) {
let isRunning;
return function(...args) {
let context = this; // store the context of the object that owns this function
if(!isRunning) {
isRunning = true;
func.apply(context,args) // execute the function with the context of the object that owns it
setTimeout(function() {
isRunning = false;
}, delay);
}
}
}
function myFunc(param) {
console.log(`Called ${this.name} at ${param}th second`);
}
let obj = {
name: "THROTTLED FUNCTION ",
throttleFunc: throttle(myFunc, 3000)
}
function handleClick() {
obj.throttleFunc(new Date().getSeconds());
}
button {
width: 100px;
height: 50px;
font-size: 20px;
}
<button onclick="handleClick()">Click me</button>
컨텍스트나 인수를 전달하지 않으려면 다음과 같은 간단한 버전이 필요합니다.
function throttle(func, delay) {
let isRunning;
return function() {
if(!isRunning) {
isRunning = true;
func()
setTimeout(function() {
isRunning = false;
}, delay);
}
}
}
function myFunc() {
console.log('Called');
}
let throttleFunc = throttle(myFunc, 3000);
function handleClick() {
throttleFunc();
}
button {
width: 100px;
height: 50px;
font-size: 20px;
}
<button onclick="handleClick()">Click me</button>
호출할 함수가 1개밖에 없을 때 간단한 해결책도 제안하고 싶습니다(예: 검색).
여기 내가 내 프로젝트에서 한 일이 있습니다.
let throttle;
function search() {
if (throttle) {
clearTimeout(throttle);
}
throttle = setTimeout(() => {
sendSearchReq(str)
}, 500);
}
입력 변경 이벤트에서 검색이 호출됩니다.
function throttle(targetFunc, delay){
let lastFunc;
let lastTime;
return function(){
const _this = this;
const args = arguments;
if(!lastTime){
targetFunc.apply(_this, args);
lastTime = Date.now();
} else {
clearTimeout(lastFunc);
lastFunc = setTimeout(function(){
targetFunc.apply(_this, args);
lastTime = Date.now();
}, delay - (Date.now() - lastTime));
}
}
}
사용해 보십시오.
window.addEventListener('resize', throttle(function() {
console.log('resize!!');
}, 200));
const { now } = Date;
export default function throttle(func, frameDuration) {
let timeout = null;
let latest;
const epoch = now();
function getDurationToNextFrame() {
const elapsed = now() - epoch;
const durationSinceLastFrame = elapsed % frameDuration;
return frameDuration - durationSinceLastFrame;
}
function throttled(...args) {
latest = () => {
func.apply(this, args);
};
if (!timeout) {
timeout = setTimeout(() => {
latest();
timeout = null;
}, getDurationToNextFrame());
}
}
return throttled;
}
단순 스로틀 기능 -
참고 - 버튼을 계속 클릭하면 처음에 클릭할 때 콘솔 로그가 표시되고 5초마다 계속 클릭할 때까지 표시됩니다.
HTML -
<button id='myid'>Click me</button>
자바스크립트 -
const throttle = (fn, delay) => {
let lastTime = 0;
return (...args) => {
const currentTime = new Date().getTime();
if((currentTime - lastTime) < delay) {
return;
};
lastTime = currentTime;
return fn(...args);
}
};
document.getElementById('myid').addEventListener('click', throttle((e) => {
console.log('I am clicked');
}, 5000));
깃발을 사용해 구현할 수도 있습니다.
var expensive = function(){
console.log("expensive functionnns");
}
window.addEventListener("resize", throttle(expensive, 500))
function throttle(expensiveFun, limit){
let flag = true;
return function(){
let context = this;
let args = arguments;
if(flag){
expensiveFun.apply(context, args);
flag = false;
setTimeout(function(){
flag = true;
}, limit);
}
}
}
@clément-prevost 답변의 약간 현대화되고 단순화된 버전은 다음과 같습니다.
function throttle(func, wait, options = {}) {
let timeout = null;
let previous = 0;
const later = (...args) => {
previous = options.leading === false ? 0 : Date.now();
func(...args);
};
return (...args) => {
const now = Date.now();
if (!previous && options.leading === false) {
previous = now;
}
const remaining = wait - (now - previous);
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
func(...args);
} else if (options.trailing !== false) {
clearTimeout(timeout);
timeout = setTimeout(() => later(...args), remaining);
}
};
}
function myFunc(a) {
console.log(`Log: ${a} ${this.val}`);
}
const myFuncThrottled = throttle(myFunc.bind({val: 42}), 1234, {leading: true, trailing: true})
myFuncThrottled(1)
myFuncThrottled(2)
myFuncThrottled(3)
function throttle(CB,ms=300,Id='Identifier for the callback(CB)'){
Id = Id || ""+CB
var N = throttle.N = throttle.N || {}; // Static variable N to store all callbacks ids and their status
if( N[Id] ) return; // already in the queue to run
N[Id] = 1; // add it the queue
setTimeout(()=>{
N[Id] = 0; // remove it from the queue
CB(); // finally call the function
}, ms);
}
for(var i=0;i<100;i++){
throttle(e=>console.log("Hi1"),1e3,'F1');
}
// will only output : Hi1
// this function guarantee the callback to run at least once
여기에 이미 몇 가지 훌륭한 솔루션이 있지만, 저는 각 함수 호출에 제공되는 마지막 인수와 함께 후행(및 선택적으로 선행) 실행 기능이 있는 최신 버전을 찾고 있었습니다.
const throttle = (fn, wait=500, leading=true) => {
let prev, timeout, lastargs;
return (...args) => {
lastargs = args;
if (timeout) return;
timeout = setTimeout(() => {
timeout = null;
prev = Date.now();
// let's do this ... we'll release the stored args as we pass them through
fn.apply(this, lastargs.splice(0, lastargs.length));
// some fancy timing logic to allow leading / sub-offset waiting periods
}, leading ? prev && Math.max(0, wait - Date.now() + prev) || 0 : wait);
};
}
용도:
x = throttle((...args) => console.log(...args));
let n = 0;
x(++n, 'boom');
x(++n, 'boom');
x(++n, 'boom');
하나씩 정의하는 함수가 하나 이상 있으면 유지할 수 없기 때문에 도우미 클래스를 사용하여 각각의 값을 유지하는 것이 좋습니다.
class slowDown {
constructor(cb,timeGap){
this.last = 0
this.run = function(){
let current = Date.now(),
shouldRun = (current - this.last) >= timeGap
if(shouldRun){
cb(current - this.last)
this.last = current
}
}
}
}
// example use
const press = new slowDown(timeElapsed => {
// define function here which you wanted to slow down
console.log("pressed after " + timeElapsed + " ms")
},750)
window.addEventListener("keydown",()=>{
press.run()
})
아래는 13 LOC에서 생각할 수 있는 가장 간단한 스로틀입니다.함수를 호출할 때마다 시간 초과가 발생하고 이전 함수를 취소합니다.원래 함수는 예상대로 적절한 컨텍스트와 인수로 호출됩니다.
function throttle(fn, delay) {
var timeout = null;
return function throttledFn() {
window.clearTimeout(timeout);
var ctx = this;
var args = Array.prototype.slice.call(arguments);
timeout = window.setTimeout(function callThrottledFn() {
fn.apply(ctx, args);
}, delay);
}
}
// try it out!
window.addEventListener('resize', throttle(function() {
console.log('resize!!');
}, 200));
언급URL : https://stackoverflow.com/questions/27078285/simple-throttle-in-javascript
'programing' 카테고리의 다른 글
뷰에 null이 아닌 열을 만드는 방법 (0) | 2023.09.11 |
---|---|
먼저 개체 파일에 컴파일해야 하는 이유는 무엇입니까? (0) | 2023.09.11 |
WonderPlugin 슬라이더에서 wonderplugin.com 링크를 제거하는 방법 (0) | 2023.09.11 |
How to export data to CSV in PowerShell? (0) | 2023.09.11 |
SQL ID는 같지만 열 값이 다른 두 행 병합(Oracle) (0) | 2023.09.11 |