javascript - Skillnaden mellan att throttla och att debounca en funktion


Kan någon ge mig en enkel-för-ord-förklaring om skillnaden mellan att throttla och debounca en funktion för syftet att begränsa hastigheten?

För mig verkar båda göra samma sak. Jag har kollat dessa två bloggar för att ta reda på det, men jag har fortfarande svårt att förstå.

http://remysharp.com/2010/07/21/throttling-function-calls

http://benalman.com/projects/jquery-throttle-debounce-plugin/

Svar #1

För att uttrycka det enkelt:

  • Strypning kommer att fördröja körningen av en funktion. Det kommer att minska antalet notifieringar från en händelse som utlöses flera gånger.
  • Debouncing kommer att slå ihop en serie av sekventiella anrop till en funktion till ett enda anrop till den funktionen. Det säkerställer att ett meddelande görs för en händelse som inträffar flera gånger.

Du kan visuellt se skillnaden i denna demonstration, som visar när en debounced respektive throttled händelse skulle utlösas baserat på musens rörelse.

Om du har en funktion som kallas ofta - till exempel när ett ändrings- eller musrörelsehändelse inträffar, kan den kallas många gånger. Om du inte vill ha detta beteende kan du Throttle den så att funktionen kallas med jämna mellanrum. Debouncing innebär att den kallas i slutet (eller början) av en rad händelser.

Svar #2

Skillnader

Debounce 1 sekundSpjäll 1 sekund
Returnerar om…1 sekund har förflutit sedan senaste inmatning1 sekund har förflutit sedan senaste utmatning
Fördröjning mellan…inmatning och utmatningutdata och utdata

Förklaring efter användningsfall

  • Sökfält: Vill du inte söka efter ett begrepp varje gång användaren trycker på en tangent? Vill du istället söka när användaren har slutat skriva i 1 sekund? Använd debounce 1 sekund vid tangenttryckning.
  • Skjutspel: Vill du inte skjuta med en pistol varje gång användaren klickar med musen? Vill du istället skjuta högst en gång varje sekund? Använd throttle 1 sekund vid musklick.

Att byta roller

  • Begränsar sökningen i 1 sekund: Om en användare skriver "abcde", med varje följande tecken som skrivs var 0,6:e sekund, kommer throttle att trigga en sökning vid trycket på "a". Den kommer sedan ignorera varje tryck under nästa 1 sekund, så "b" vid 0,6 sekunder kommer att ignoreras. Sedan kommer den återigen trigga en sökning vid trycket på "c" vid 1,2 sekunder, vilket återställer tiden. "d" vid 1,8 sekunder kommer att ignoreras. Slutligen kommer den återigen trigga en sökning vid trycket på "e" vid 2,4 sekunder.
  • Debouncing pistolen i 1 sekund: När en användare ser en fiende klickar han med musen, men debounce kommer inte att avfyra pistolen. Han kommer att klicka flera gånger under den sekunden men det kommer fortfarande att ignoreras. Först efter att han har slutat klicka i en hel sekund kommer pistolen äntligen att avfyras, ett enda skott.

En "verklighetsbaserad" jämförelse

Det finns några vakter utanför en bar. Vakter släpper in en person som säger "Jag vill gå in" i baren. Detta är ett normalt scenario. Alla som säger "Jag vill gå in" får gå in i baren.

Nu finns det ett Gaspedalvakt (gaspedal 5 sekunder). Han gillar människor som svarar först. Den som säger "Jag går" först, tillåter han in. Sedan avvisar han varje person under de nästa 5 sekunderna. Efter det kommer återigen den som säger det först att tillåtas och andra kommer att avvisas under de nästa 5 sekunderna.

Det finns en annan vakt, Debounce-vakten (debounce 5 sekunder). Han gillar personer som ger honom mental vila i 5 sekunder. Så om någon säger "Jag kommer", väntar vakten i 5 sekunder. Om ingen annan stör honom under de 5 sekunderna, släpper han in den första personen. Men, om någon annan avbryter och säger "Jag kommer" under de 5 sekunderna, kommer han att avvisa den första personen och börja om 5-sekunders väntan, denna gång för den andra personen. Om 5 sekunder går utan avbrott nu, släpps den andra personen in, men inte den första.

Svar #3

Personligen tyckte jag att debounce var svårare att förstå än throttle.

Eftersom båda funktionerna hjälper dig att skjuta upp och minska hastigheten för viss exekvering. Förutsatt att du anropar dekorerade funktioner som returneras av throttle/debounce upprepade gånger...

  • Throttle: den ursprungliga funktionen kommer att anropas högst en gång per angiven period.
  • Debounce: den ursprungliga funktionen kommer att anropas efter att anroparen slutar anropa den dekorerade funktionen efter en angiven period.

Jag tyckte att den sista delen av debounce var avgörande för att förstå målet det försöker uppnå. Jag hittade också en gammal version av implementationen av _.debounce som hjälper förståelsen (tack vare https://davidwalsh.name/function-debounce).

// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_.debounce = function(func, wait, immediate) {
  var timeout;
  return function() {
    var context = this, args = arguments;
    var later = function() {
        timeout = null;
        if (!immediate) func.apply(context, args);
    };
    var callNow = immediate && !timeout;
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
    if (callNow) func.apply(context, args);
  };
};

En långsökt metafor, men kanske kan den också hjälpa.

Du har en vän som heter Chatty som gillar att prata med dig via IM. Om man antar att när hon pratar skickar hon ett nytt meddelande var femte sekund, medan ikonen för din IM-applikation studsar upp och ner, kan du ta...

  • Naiv metod: kontrollera varje meddelande så snart det anländer. När din appikon studsar, kontrollera. Det är inte det mest effektiva sättet, men du är alltid uppdaterad.
  • Throttle-metod: du kollar en gång var 5:e minut (när det finns nya). När ett nytt meddelande kommer, om du har kollat när som helst under de senaste 5 minuterna, ignorera det. Du sparar tid med detta tillvägagångssätt, samtidigt som du fortfarande är med i loopen.
  • Debounce-metod: du känner till Chatty, hon delar upp en hel historia i bitar och skickar dem i ett meddelande efter det andra. Du väntar tills Chatty har avslutat hela historien: om hon slutar skicka meddelanden i 5 minuter, skulle du anta att hon är klar, nu kontrollerar du allt.

Svar #4

Hastighetsbegränsning (1 sek): Hej, jag är en robot. Så länge du fortsätter att pinga mig kommer jag att fortsätta prata med dig, men med exakt 1 sekunds mellanrum. Om du pingar mig för ett svar innan en sekund har gått, kommer jag fortfarande att svara dig med exakt 1 sekunds intervall. Med andra ord, jag älskar bara att svara med exakta intervaller.

Debounce (1 sek): Hej, jag är den där ^^robotens kusin. Så länge du fortsätter pinga mig, kommer jag att förbli tyst eftersom jag gillar att svara först efter att 1 sekund har passerat sedan senaste gången du pingade mig. Jag vet inte om det är för att jag har ett attitydproblem eller för att jag helt enkelt inte gillar att avbryta folk. Med andra ord, om du fortsätter att be mig om svar innan 1 sekund har gått sedan ditt senaste anrop, kommer du aldrig få ett svar. Ja ja...kör på! kalla mig oartig.


Reglage (10 min): Jag är en loggningsmaskin. Jag skickar systemloggar till vår backend-server efter ett regelbundet intervall på 10 minuter.

Debounce (10 sek): Hej, jag är inte kusin till den där loggningsmaskinen. (Inte varje debouncer är relaterad till en throttler i den här imaginära världen). Jag arbetar som servitör på en närliggande restaurang. Jag bör låta dig veta att så länge du fortsätter lägga till saker i din beställning, kommer jag inte gå till köket för att utföra din beställning. Först när 10 sekunder har gått efter att du senast ändrade din beställning, kommer jag att anta att du är klar med din beställning. Först då kommer jag gå och utföra din beställning i köket.


Coola demos: https://css-tricks.com/debouncing-throttling-explained-examples/

Krediter för servitörsanalogi: https://codeburst.io/throttling-and-debouncing-in-javascript-b01cad5c8edf

Svar #5

  • Begränsning tvingar fram ett maximalt antal gånger en funktion kan anropas över tid. Som i "utför denna funktion högst en gång var 100:e millisekund."
  • Debouncing säkerställer att en funktion inte anropas igen förrän en viss tid har gått utan att den har anropats. Som i "utför den här funktionen endast om 100 millisekunder har gått utan att den har anropats."

Källa

Svar #6

Det är enklare än demonstrationerna.

När throttle kallas körs din funktion periodiskt (t.ex. var 20:e ms) - och en gång i slutet.

Debounce aktiveras endast i slutet (t.ex. 20 ms efter den senaste händelsen).

Så, så länge händelser fortsätter att utlösas snabbt (t.ex. med intervaller på mindre än 20 ms), kommer debounce förbli tyst, medan throttle kommer att utlösas en gång var 20:e ms. Båda kommer att utlösas i slutet, skillnaden är endast att throttle också kommer att utlösas däremellan med fasta intervaller.

Exempel: Om du bläddrar kommer throttle långsamt att anropa din funktion medan du bläddrar (varje X millisekunder). Debounce väntar tills du är klar med att bläddra innan den anropar din funktion (möjligen bara en gång).


Jag gillar att tänka på throttle som "inkluderande debounce", de båda gör ett slutligt anrop efter att händelserna är klara, men på grund av implementeringsdetaljer gör de två inte alltid detta slutliga anrop exakt samtidigt, vilket gör demonstrationer förvirrande.

Svar #7

I enklare termer:

Debouncing kommer att förhindra att en funktion körs medan den fortfarande anropas ofta. En debounced funktion kommer endast att köras efter att det har fastställts att den inte längre anropas, vid vilken tidpunkt den kommer att köras exakt en gång. Praktiska exempel på debouncing:

  • Autosparande eller validering av innehållet i ett textfält om användaren "slutat skriva": åtgärden kommer endast att utföras en gång, EFTER att det har fastställts att användaren inte längre skriver (inte längre trycker på tangenter).

  • Loggning av var användare vilar sin mus: användaren rör inte längre musen, så den (sista) positionen kan loggas.

Hastighetsbegränsning kommer helt enkelt att förhindra att en funktion körs om den nyligen har körts, oavsett anropsfrekvens. Praktiska exempel på hastighetsbegränsning:

  • Implementeringar av v-sync är baserade på begränsning: skärmen kommer endast att ritas om om 16 ms har gått sedan senaste skärmuppdatering. Oavsett hur många gånger skärmens uppdateringsfunktionalitet anropas, kommer den endast att köras högst en gång var 16:e millisekund.

Svar #8

En bild säger mer än tusen ord

Gasspjäll

Debounce

Observera att Debounce inte utlöses förrän händelseflödet stoppar. Däremot kommer Throttle att utlösa en händelse i varje intervall.

(Tack till css-tricks)

Svar #9

En verklighetsbaserad analogi som personligen hjälper mig att komma ihåg:

  • debounce=en konversation. du väntar på att den andra personen ska sluta tala innan du svarar.
  • throttle=ett trumslag. du spelar bara toner på ett enkelt 4/4 trumslag.

Användningsområden för debounce:

  • Skrivning. Du vill göra något efter att användaren har slutat skriva. Så att vänta 1 sekund efter det sista tangenttrycket är vettigt. Varje tangenttryckning startar om väntan.
  • Animering. Du vill krympa ett element efter att användaren slutat hovra över det. Att inte använda debounce kan orsaka en ryckig animation som ett resultat av att markören av misstag rör sig mellan "heta" och "kalla" zoner.

Användningsområden för throttle:

  • Scrollning. Du vill reagera på scrollning men begränsa mängden beräkningar som görs, så att göra något var 100:e ms är tillräckligt för att förhindra potentiell fördröjning.
  • Musrörelse. Samma som rullning men för musrörelse.
  • API-anrop Du vill göra ett API-anrop vid vissa UI-händelser men vill begränsa antalet API-anrop du gör för att inte överbelasta din server.

Svar #10

Debouncing låter dig hantera frekvensen av anrop som en funktion kan ta emot. Det kombinerar flera anrop som sker på en viss funktion så att upprepade anrop som inträffar innan en viss tidsperiod har gått ignoreras. I princip säkerställer debouncing att exakt en signal skickas för en händelse som kan ske flera gånger.

Strypning begränsar frekvensen av anrop som en funktion tar emot till ett fast tidsintervall. Det används för att säkerställa att mål-funktionen inte anropas oftare än den angivna fördröjningen. Strypning är minskningen i hastighet av en upprepad händelse.

Svar #11

Att kombinera debounce och throttle kan vara mycket förvirrande, eftersom de båda delar en parameter som kallas delay.

Debounce. Fördröjningen är för att vänta tills det inte finns fler anrop längre, och sedan anropa det. Mycket likt att stänga en hissdörr: dörren måste vänta tills ingen försöker gå in innan den stängs.

Regulator. fördröjning är att vänta med en viss frekvens, och sedan anropa för den sista. Mycket likt att avfyra en pistol, pistolen kan helt enkelt inte avfyras bortom en viss hastighet.


Låt oss titta på implementeringen för att se i detalj.

function debounce(fn, delay) {
  let handle = null
  
  return function () {
    if (handle) {
      handle = clearTimeout(handle)
    }
    
    handle = setTimeout(() => {
      fn(...arguments)
    }, delay)
  }
}

Debounce, fortsätter att avbryta timeouten tills inga fler avbrott sker, och kör sedan fn.

function throttle(fn, delay) {
  let handle = null
  let prevArgs = undefined
  
  return function() {
    prevArgs = arguments
    if (!handle) {
      fn(...prevArgs)
      prevArgs = null
      handle = setInterval(() => {
        if (!prevArgs) {
          handle = clearInterval(handle)
        } else {
          fn(...prevArgs)
          prevArgs = null
        }
      }, delay)
    }
  }
}

Throttle lagrar de senaste anropsargumenten och ställer in ett intervall för att utlösas tills inga tidigare utlösningar finns kvar.

Likheter. Båda har en fördröjningstid, och det finns ingen brand under fördröjningen, särskilt när det bara finns en brand. Båda summerar inte tidigare händelser, så antalet händelser kan vara annorlunda än de faktiska bränderna.

Skillnad. Fördröjningen kan förlängas i debounce-fallet med upprepade händelser. Medan fördröjningen i throttle-fallet är fast. Så i allmänhet får du fler utlösningar från throttle än debounce.

Lätt att komma ihåg. Debounce-grupper slår ihop anrop till ett. Throttle håller bundna anrop inom viss frekvens.

Uppdaterad 2023-01-20

Throttle kanske inte behöver setInterval, här är en ny version jag skrev nyligen som tar hand om this också.

function throttle(fn, delay) {
  let canFire = true
  let queue = []

  function pop() {
    if (queue.length < 1) return 

    const [that, args] = queue.pop()
    fn.apply(that, args)
    canFire = false
    setTimeout(() => {
      canFire = true
      pop()
    }, delay)
  }
  
  function push() {
    queue.push([this, arguments])
    if (canFire) pop()
  } 

  push.cancel = () => {
    queue = []
  }

  return push
}

Uppdaterad 11-09-23

Jag börjar tro att throttle är ett tillägg ovanpå debounce. Läs detta https://windmaomao.medium.com/throttle-is-a-debounce-add-on-80d4a6027ad4.

function throttle(fn, delay) {
  let h
  let queue = []

  function pop() {
    if (queue.length < 1) return 

    if (!h) {
      const [that, args] = queue.pop()
      fn.apply(that, args) 
      
      h = setTimeout(() => {
        h = null
        pop()
      }, delay)      
    }      
  }
  
  return function push() {
    queue.push([this, arguments])
    pop()
  } 
}

Relaterade artiklar