Heim  >  Artikel  >  Web-Frontend  >  Entprellen und Drosseln

Entprellen und Drosseln

王林
王林Original
2024-08-09 20:32:30483Durchsuche

Debouncing and Throttling

Eine weitere beliebte Frontend-Interviewfrage. Es testet das Wissen der Befragten zu JS, Leistung und FE-Systemdesign.

Dies ist Frage Nr. 2 der Reihe „Frontend-Interviewfragen“. Wenn Sie Ihre Vorbereitung verbessern oder allgemein auf dem Laufenden bleiben möchten, sollten Sie sich bei FrontendCamp anmelden.


Entprellen und Drosseln funktionieren nach dem gleichen Prinzip – Verzögerungsdinge –, haben aber dennoch sehr unterschiedliche Ansätze und Anwendungsfälle.

Beide Konzepte sind nützlich für die Entwicklung einer leistungsstarken Anwendung. Fast alle Websites, die Sie täglich besuchen, nutzen auf die eine oder andere Weise Entprellen und Drosseln.

Entprellen

Ein bekannter Anwendungsfall des Entprellens ist ein Typeahead (oder eine automatische Vervollständigung).

Stellen Sie sich vor, Sie erstellen eine Suchfunktion für eine E-Commerce-Website mit Tausenden von Produkten. Wenn ein Benutzer versucht, nach etwas zu suchen, führt Ihre App einen API-Aufruf durch, um alle Produkte abzurufen, die mit der Abfragezeichenfolge des Benutzers übereinstimmen.

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

Dieser Ansatz sieht gut aus, weist jedoch einige Probleme auf:

  1. Sie führen bei jedem Tastendruckereignis einen API-Aufruf durch. Wenn ein Benutzer 15 Zeichen eingibt, sind das 15 API-Aufrufe für einen einzelnen Benutzer. Das würde nie skalieren.
  2. Wenn das Ergebnis dieser 15 API-Aufrufe eintrifft, benötigen Sie nur noch den letzten. Das Ergebnis der vorherigen 14 Aufrufe wird verworfen. Es verbraucht viel Bandbreite des Benutzers und Benutzer in einem langsamen Netzwerk werden eine erhebliche Verzögerung bemerken.
  3. Auf der Benutzeroberfläche lösen diese 15 API-Aufrufe ein erneutes Rendern aus. Dadurch wird die Komponente verzögert.

Die Lösung für diese Probleme ist Entprellen.

Die Grundidee besteht darin, zu warten, bis der Benutzer mit der Eingabe aufhört. Wir werden den API-Aufruf verzögern.

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

Wir haben unseren bestehenden Code erweitert, um die Entprellung zu nutzen.

Die Debounce-Funktion ist eine generische Hilfsfunktion, die zwei Argumente benötigt:

  1. fn: Der Funktionsaufruf, der verzögert werden soll.
  2. Verzögerung: Die Verzögerung in Millisekunden.

Innerhalb der Funktion verwenden wir setTimeout, um den eigentlichen Funktionsaufruf (fn) zu verzögern. Wenn der Fn erneut aufgerufen wird, bevor der Timer abgelaufen ist, wird der Timer zurückgesetzt.

Mit unserer aktualisierten Implementierung würden wir, selbst wenn der Benutzer 15 Zeichen eingibt, nur einen API-Aufruf durchführen (vorausgesetzt, jeder Tastendruck dauert weniger als 500 Millisekunden). Dies löst alle Probleme, die wir hatten, als wir mit der Entwicklung dieser Funktion begannen.

In einer Produktionscodebasis müssen Sie keine eigene Entprellungsdienstprogrammfunktion programmieren. Möglicherweise verwendet Ihr Unternehmen bereits eine JS-Dienstprogrammbibliothek wie lodash, die über diese Methoden verfügt.

Drosselung

Nun, Debounce ist großartig für die Leistung, aber es gibt einige Szenarien, in denen wir nicht x Sekunden warten möchten, bevor wir über eine Änderung benachrichtigt werden.

Stellen Sie sich vor, Sie erstellen einen kollaborativen Arbeitsbereich wie Google Docs oder Figma. Eines der Hauptmerkmale ist, dass ein Benutzer in Echtzeit über Änderungen informiert sein sollte, die andere Benutzer vorgenommen haben.

Bisher kennen wir nur zwei Ansätze:

  1. Der Noob-Ansatz: Jedes Mal, wenn ein Benutzer seinen Mauszeiger bewegt oder etwas eingibt, führen Sie einen API-Aufruf durch. Sie wissen bereits, wie schlimm es werden kann.
  2. Der Debounce-Ansatz: Er löst zwar die Leistungsseite, ist aber aus UX-Perspektive schrecklich. Ihr Kollege schreibt möglicherweise einen Absatz mit 300 Wörtern und Sie werden am Ende nur einmal benachrichtigt. Gilt es immer noch als Echtzeit?

Hier kommt Throttling ins Spiel. Es liegt genau in der Mitte der beiden oben genannten Ansätze. Die Grundidee besteht darin, in regelmäßigen Abständen zu benachrichtigen – nicht am Ende und nicht bei jedem Tastendruck, sondern in regelmäßigen Abständen.

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

Wir haben unseren vorhandenen Code geändert, um die Drosselfunktion zu nutzen. Es sind zwei Argumente erforderlich:

  1. fn: Die eigentliche Funktion zum Drosseln.
  2. Zeit: Das Intervall, nach dem die Funktion ausgeführt werden darf.

Die Implementierung ist unkompliziert. Den Zeitpunkt des letzten Aufrufs der Funktion speichern wir in lastCalledAt. Wenn das nächste Mal ein Funktionsaufruf erfolgt, prüfen wir, ob die Zeit vergangen ist, und führen erst dann fn.

aus

Wir haben es fast geschafft, aber diese Implementierung weist einen Fehler auf. Was passiert, wenn der letzte Funktionsaufruf mit einigen Daten innerhalb des Zeitintervalls erfolgt und danach kein Aufruf mehr erfolgt? Bei unserer aktuellen Implementierung gehen einige Daten verloren.

Um dies zu beheben, speichern wir die Argumente in einer anderen Variablen und initiieren eine Zeitüberschreitung, die später aufgerufen wird, wenn kein Ereignis empfangen wird.

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

Diese aktualisierte Implementierung stellt sicher, dass uns keine Daten entgehen.

Lodash bietet auch eine Drosselfunktion.


Zusammenfassung

  1. Entprellen und Drosseln sind Techniken zur Leistungsoptimierung.
  2. Beide funktionieren nach einem ähnlichen Prinzip – Dinge verzögern.
  3. Debounce wartet auf t, nachdem das letzte Ereignis empfangen wurde, während Throttling den Fn periodisch in t Zeit ausführt.
  4. Entprellen wird in Suchfunktionen verwendet und Drosselung wird in Echtzeit-Apps verwendet (nicht darauf beschränkt).

Ressourcen

FrontendCamp
lodash

Das obige ist der detaillierte Inhalt vonEntprellen und Drosseln. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn