Simple throttle in JavaScript

JavascriptJqueryThrottling

Javascript Problem Overview


I am looking for a simple throttle in JavaScript. I know libraries like lodash and underscore have it, but only for one function it will be overkill to include any of those libraries.

I was also checking if jQuery has a similar function - could not find.

I have found one working throttle, and here is the code:

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);
    }
  };
}

The problem with this is: it fires the function once more after the throttle time is complete. So let's assume I made a throttle that fires every 10 seconds on keypress - if I do keypress 2 times, it will still fire the second keypress when 10 seconds are completed. I do not want this behavior.

Javascript Solutions


Solution 1 - Javascript

I would use the [underscore.js][1] or [lodash][2] source code to find a well tested version of this function.

Here is the slightly modified version of the underscore code to remove all references to underscore.js itself:

// 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;
  };
};

Please note that this code can be simplified if you don't need all the options that underscore support.

Please find below a very simple and non-configurable version of this function:

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);
        }
    }
}

Edit 1: Removed another reference to underscore, thx to @Zettam 's comment

Edit 2: Added suggestion about lodash and possible code simplification, thx to @lolzery @wowzery 's comment

Edit 3: Due to popular requests, I added a very simple, non-configurable version of the function, adapted from @vsync 's comment

[1]: https://github.com/jashkenas/underscore/blob/master/underscore.js "underscore.js" [2]: https://lodash.com

Solution 2 - Javascript

What about this?

function throttle(func, timeFrame) {
  var lastTime = 0;
  return function () {
      var now = Date.now();
      if (now - lastTime >= timeFrame) {
          func();
          lastTime = now;
      }
  };
}

Simple.

You may be interested in having a look at the source.

Solution 3 - Javascript

callback: takes the function that should be called

limit: number of times that function should be called within the time limit

time: time span to reset the limit count

functionality and usage: Suppose you have an API that allows user to call it 10 times in 1 minute

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);

Solution 4 - Javascript

Adding to the discussion here (and for more recent visitors), if the reason for not using the almost de facto throttle from lodash is to have a smaller sized package or bundle, then it's possible to include only throttle in your bundle instead of the entire lodash library. For example in ES6, it would be something like:

import throttle from 'lodash/throttle';

Also, there is a throttle only package from lodash called lodash.throttle which can be used with a simple import in ES6 or require in ES5.

Solution 5 - Javascript

Here's how I implemented throttle function in ES6 in 9LOC, hope it helps

function throttle(func, delay) {
  let timeout = null
  return function(...args) {
    if (!timeout) {
      timeout = setTimeout(() => {
        func.call(this, ...args)
        timeout = null
      }, delay)
    }
  }
}

Click on this link to see how it works.

Solution 6 - Javascript

I've just needed a throttle/debounce function for window resize event, and being curious, I also wanted to know what these are and how they work.

I've read multiple blog posts and QAs on SO, but they all seem to overcomplicate this, suggest libraries, or just provide descriptions and not simple plain JS implementations.

I won't provide a description since it's plentiful. So here's my implementation:

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);
    }
}

These might need tweaks (e.g., initially the callback isn't called immediately).

See the difference in action (try resizing the window):

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>

JSFiddle

Solution 7 - Javascript

I've seen a lot of answers here that are way too complex for "a simple throttle in js".

Almost all of the simpler answers just ignore calls made "in throttle" instead of delaying execution to the next interval.

Here's a simple implementation that also handles calls "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
  }
}

This is almost the exact same implementation for a simple debounce. It just adds a calculation for the timeout delay which requires tracking when the function was last ran. See below:

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
  }
}

Solution 8 - Javascript

Simple solution in ES6. Codepen Demo

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>

Solution 9 - Javascript

I made a npm package with some throttling functions:

> npm install function-throttler

throttleAndQueue

Returns a version of your function that can be called at most every W milliseconds, where W is wait. Calls to your func that happen more often than W get queued up to be called every W ms

throttledUpdate

Returns a version of your function that can be called at most every W milliseconds, where W is wait. for calls that happen more often than W the last call will be the one called (last takes precedence)

throttle

limits your function to be called at most every W milliseconds, where W is wait. Calls over W get dropped

Solution 10 - Javascript

There is a library suited for this purpose, it's Backburner.js from Ember.

https://github.com/BackburnerJS/

You'd use it so.

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)

Solution 11 - Javascript

Here's my own version of Vikas post:

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);
        }
    };
}

I find that using setInterval is not a good idea.

Solution 12 - Javascript

This throttle function is build on ES6. Callback functions takes arguments (args), and still it works wrapped with throttle function. Be free to customize delay time according to your app needs. 1 time per 100ms is used for development mode, event "oninput" is just an example for frequent case of its use:

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));

P.S. As @Anshul explained: throttling enforces a maximum number of times a function can be called over time. As in "execute this function at most once every 100 milliseconds."

Solution 13 - Javascript

In below example, try clicking the button multiple times, but the myFunc function would be executed only once in 3 sec. The function throttle is passed with the function to be executed and the delay.It returns a closure, which is stored in obj.throttleFunc. Now since obj.throttleFunc stores a closure, the value of isRunning is maintained inside it.

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>


> If we don't want the context or arguments to be passed, then a simpler > version of this would be as following:

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>

Solution 14 - Javascript

I also want to suggest a simple solution for when there is only 1 function you know you will call (for example: Search)

here is what i did in my project

let throttle;

function search() {
    if (throttle) {
      clearTimeout(throttle);
    }
    throttle = setTimeout(() => {
      sendSearchReq(str)
    }, 500);
  }

Search is called on input change event

Solution 15 - Javascript

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));
    }
  }
}

Try it :

window.addEventListener('resize', throttle(function() {
  console.log('resize!!');
}, 200));

Solution 16 - Javascript

Simple throttle function -

Note- Keep on clicking on the button , You'll see console log at first on click and then only after every 5 seconds until you're keep clicking.

HTML -

<button id='myid'>Click me</button>

Javascript -

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));

Solution 17 - Javascript

We can also implement using a flag-

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);
		}
	}
}

Solution 18 - Javascript

Here is a bit modernized and simplified version of @clément-prévost answer

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)

Solution 19 - Javascript

CodeSandbox

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;
}

Solution 20 - Javascript

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 

Solution 21 - Javascript

Some great solutions here already, but I was looking for a modern version with trailing (and optionally leading) executions, with the last passed arguments provided to each function call:

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);
  };
}

Usage:

x = throttle((...args) => console.log(...args));
let n = 0;
x(++n, 'boom');
x(++n, 'boom');
x(++n, 'boom');

Solution 22 - Javascript

if there will be more than one function defining them one by one would not be maintainable so i would suggest use a helper class to keep values for each

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()
})

Solution 23 - Javascript

Below is the simplest throttle I could think of, in 13 LOC. It creates a timeout each time the function is called and cancels the old one. The original function is called with the proper context and arguments, as expected.

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));

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMiaView Question on Stackoverflow
Solution 1 - JavascriptClément PrévostView Answer on Stackoverflow
Solution 2 - JavascriptsmartmouseView Answer on Stackoverflow
Solution 3 - JavascriptVikas BansalView Answer on Stackoverflow
Solution 4 - JavascriptDivyanshu MaithaniView Answer on Stackoverflow
Solution 5 - Javascriptvipin goyalView Answer on Stackoverflow
Solution 6 - JavascriptakinuriView Answer on Stackoverflow
Solution 7 - JavascriptrsimpView Answer on Stackoverflow
Solution 8 - JavascriptvaskortView Answer on Stackoverflow
Solution 9 - JavascriptAlmenonView Answer on Stackoverflow
Solution 10 - JavascriptRainbView Answer on Stackoverflow
Solution 11 - JavascriptmakoreView Answer on Stackoverflow
Solution 12 - JavascriptRomanView Answer on Stackoverflow
Solution 13 - JavascriptAyanView Answer on Stackoverflow
Solution 14 - JavascriptDan LevinView Answer on Stackoverflow
Solution 15 - JavascriptBeingSumanView Answer on Stackoverflow
Solution 16 - JavascriptAvadhut ThoratView Answer on Stackoverflow
Solution 17 - Javascriptganesh phirkeView Answer on Stackoverflow
Solution 18 - JavascriptSerhii HolineiView Answer on Stackoverflow
Solution 19 - JavascriptIzhakiView Answer on Stackoverflow
Solution 20 - JavascriptSaifView Answer on Stackoverflow
Solution 21 - JavascriptsomView Answer on Stackoverflow
Solution 22 - JavascriptAmir RahmanView Answer on Stackoverflow
Solution 23 - JavascriptalessioalexView Answer on Stackoverflow