JavaScript: Debounce & Throttle

JavaScript: Debounce & Throttle

Einer der Dinge, die JavaScript ausmachen, ist das Reagieren auf Events. So l├Ąsst sich eine bestimmte Funktion ausf├╝hren, wenn die Gr├Â├če des Fensters ver├Ąndert wird oder wenn gescrollt wird. Es hat aber auch seine T├╝cken: Je nach Komplexit├Ąt der auszuf├╝hrenden Funktion und der Geschwindigkeit des Auftretens der Events k├Ânnen die Auswirkungen auf die Performance verheerend sein - unfl├╝ssige Scrollbewegungen, langsame Animationen bis hin zum kompletten Einfrieren des Browsers

Debounce und throttle Funktionen bieten hier Abhilfe. Der Sinn dieser beiden Funktionen ist es mehrere Events zu b├╝ndeln und die Menge der Funktionsaufrufe zu reduzieren. Der Unterschied zwischen den Beiden liegt hierbei nur in der Weise, wie verschiedene Events geb├╝ndelt und wann die Funktion ausgef├╝hrt wird.

Theoretisches

Das hier beschriebene Verhalten kann in der dazu eingerichteten Demoseite visualisiert werden.

Throttle

Eine throttle Funktion bietet die M├Âglichkeit viele auftretende Events zu "bremsen" und diese regelm├Ą├čig auszuf├╝hren. Wird eine throttle Funktion mit einem Intervall von 15ms definiert, so wird bei einer "Flut" an Events die definierte Funktion regelm├Ą├čig, alle 15ms, ausgef├╝hrt.

Debounce

Eine debounce Funktion wartet auf Events und f├╝hrt die auszuf├╝hrende Funktion erst aus, wenn nach einer gewissen Zeit kein neues Event ausgel├Âst wurde. Wird eine debounce Funktion mit einer Zeit von 15ms definiert, so wird so lange die Funktion nicht ausgef├╝hrt, bis das Event f├╝r 15ms nicht mehr gefeuert wird. Wird auf die Bewegung der Maus geh├Ârt und der Nutzer bewegt seine Maus gleichm├Ą├čig f├╝r 1 Minute, so wird die Funktion w├Ąhrend der ganzen Minute nicht ausgef├╝hrt.

Implementierung

Hinweis: Die hier gezeigten Funktionen sind keine Eigenentwicklungen, sondern wurden von Remy Sharp ├╝bernommen. Die debounce Funktion wurde entsprechend angepasst, um konsistent mit der throttle Funktion zu sein (context als letzten Parameter, anstatt imidiate).

 

var helpers = {
  /**
   * debouncing, executes the function if there was no new event in $wait milliseconds
   * @param func
   * @param wait
   * @param scope
   * @returns {Function}
   */
  debounce: function(func, wait, scope) {
    var timeout;
    return function() {
      var context = scope || this, args = arguments;
      var later = function() {
        timeout = null;
        func.apply(context, args);
      };
      clearTimeout(timeout);
      timeout = setTimeout(later, wait);
    };
  },
  
  /**
   * In case of a "storm of events", this executes once every $threshold
   * @param fn
   * @param threshold
   * @param scope
   * @returns {Function}
   */
  throttle: function(fn, threshold, scope) {
    threshold || (threshold = 250);
    var last, deferTimer;
    
    return function() {
      var context = scope || this;
      var now = +new Date, args = arguments;
      
      if (last && now < last + threshold) {
        // Hold on to it
        clearTimeout(deferTimer);
        deferTimer = setTimeout(function() {
          last = now;
          fn.apply(context, args);
        }, threshold);
      } else {
        last = now;
        fn.apply(context, args);
      }
    };
  }
}

var resizeHandler = function(){
  this.doSomeMagic();
  this.blackMagic();
}

// Debounce by waiting 0.25s (250ms) with "this" context
window.addEventListener('resize', helpers.debounce(resizeHandler, 250, this));

 

Echte vs. gef├╝hlte Performance bei throttle

Es steht au├čer Frage, dass in vielen F├Ąllen eine throttle Funktion die Performance, besonders auf schw├Ącheren Ger├Ąten, erh├Âhen kann, da weniger Funktionsaufrufe stattfinden. Hier ist jedoch der Einsatzzweck wichtig: sollen durch die Funktion sanfte Animationen realisiert werden, kann durch einen falsch gew├Ąhlten threshold der Eindruck von schlechter Performance durch niedrige Bildraten entstehen. F├╝r eine fl├╝ssig wirkende Animation sollte dabei min. 30fps vorhanden sein. 

 

// Calculate frames per second based on threshold
var threshold = 50;
var fps = 1000 / threshold // (max. 20fps - a lower threshold would make more sense)

// Calculate threshold based on frames per second
var maxfps = 60;
var threshold = 1000 / maxfps;

 


├ťber uns

land in sicht bietet digitale Lo╠łsungen fu╠łr Destinationen und Leistungstra╠łger im Tourismus: toubiz┬«-Infosystem fu╠łr touristische Infrastruktur, Webportale und das Frontend fu╠łr das TOMAS┬« Buchungssystem.

Standort Deutschland

Br├╝hlmatten 16
79295 Sulzburg

Telefon +49 7634 56956-0

Standort Schweiz

Letzistrasse 29
9015 St. Gallen

Telefon ÔÇş+41 71 571 069-0ÔÇČ