Interesuje mnie funkcja "debouncing" w javascript, napisana tutaj: http://davidwalsh.name/javascript-debounce-function
Niestety kod nie jest wystarczająco jasny, abym mógł go zrozumieć. Czy ktoś może mi pomóc dowiedzieć się, jak to działa (zostawiłem swoje komentarze poniżej). Krótko mówiąc, po prostu naprawdę nie rozumiem, jak to działa
// 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.
function debounce(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);
};
};
EDYCJA: skopiowany fragment kodu znajdował się wcześniej callNow
w niewłaściwym miejscu.
javascript
debouncing
Startec
źródło
źródło
clearTimeout
z czymś, co nie jest prawidłowym identyfikatorem timera, nic nie robi.WindowTimers
obiektu, dla którego metoda została wywołana, metoda nic nie robi."Odpowiedzi:
Kod w pytaniu został nieznacznie zmieniony w stosunku do kodu w linku. W linku znajduje się czek
(immediate && !timeout)
PRZED utworzeniem nowego limitu czasu. Posiadanie go po powoduje, że tryb natychmiastowy nigdy nie uruchamia się. Zaktualizowałem moją odpowiedź, aby dodać adnotację do wersji roboczej z linku.źródło
immediate && timeout
sprawdzenia. Czy nie zawsze będzietimeout
(ponieważtimeout
jest nazywany wcześniej). Poza tym, co dobrego robiclearTimeout(timeout)
, kiedy zostanie zadeklarowane (czyniąc to niezdefiniowanym) i wyczyszczone, wcześniejimmediate && !timeout
Wyboru jest, gdy nieczułości jest skonfigurowany zimmediate
flagą. Spowoduje to natychmiastowe wykonanie funkcji, ale nałożywait
limit czasu, zanim będzie można wykonać ponownie. Tak więc!timeout
część w zasadzie mówi „przepraszam, koleś, to zostało już wykonane w zdefiniowanym oknie” ... pamiętaj, że funkcja setTimeout wyczyści to, pozwalając na wykonanie następnego wywołania.setTimeout
funkcji? Ponadto wypróbowałem ten kod, dla mnie przekazanietrue
natychmiastowego po prostu zapobiega w ogóle wywołaniu funkcji (zamiast wywoływania z opóźnieniem). Czy to się dzieje dla Ciebie?Ważną rzeczą do zapamiętania jest to, że
debounce
tworzy funkcję, która jest „zamknięta” natimeout
zmiennej. Tetimeout
zmienne pobyty dostępne podczas każdej rozmowy wytworzonego funkcji nawet podebounce
sam powrócił, i może ulegać zmianom w różnych połączeń.Ogólny pomysł
debounce
jest następujący:Pierwsza kwestia jest sprawiedliwa
var timeout;
, rzeczywiście jest sprawiedliwaundefined
. Na szczęścieclearTimeout
jest dość luźny, jeśli chodzi o dane wejściowe: przekazanieundefined
identyfikatora timera powoduje, że po prostu nic nie robi, nie zgłasza błędu ani czegoś takiego.Drugi punkt dotyczy utworzonej funkcji. Najpierw przechowuje niektóre informacje o wywołaniu (
this
kontekst iarguments
) w zmiennych, dzięki czemu może później użyć ich w wywołaniu zdemontowanym. Następnie czyści limit czasu (jeśli był jeden zestaw), a następnie tworzy nowy, aby go zastąpić za pomocąsetTimeout
. Zauważ, że powoduje to nadpisanie wartościtimeout
i ta wartość będzie trwała przez wiele wywołań funkcji! Dzięki temu debounce faktycznie działa: jeśli funkcja jest wywoływana wiele razy,timeout
jest wielokrotnie nadpisywana nowym zegarem. Gdyby tak nie było, wiele wywołań spowodowałoby uruchomienie wielu timerów, z których wszystkie pozostałyby aktywne - wywołania byłyby po prostu opóźnione, ale nie byłyby odrzucane.Trzeci punkt jest wykonywany w wywołaniu zwrotnym limitu czasu. Cofa
timeout
zmienną i wykonuje rzeczywiste wywołanie funkcji przy użyciu przechowywanych informacji o wywołaniu.immediate
Flaga ma kontrolować, czy funkcja powinna zostać wywołana przed lub po timera. Jeśli takfalse
, oryginalna funkcja jest wywoływana dopiero po uderzeniu licznika czasu. Jeśli tak jesttrue
, pierwotna funkcja jest najpierw wywoływana i nie będzie wywoływana więcej, dopóki zegar nie zostanie uderzony.Uważam jednak, że
if (immediate && !timeout)
sprawdzenie jest błędne:timeout
właśnie został ustawiony na identyfikator zegara zwrócony przez,setTimeout
więc!timeout
jest zawszefalse
w tym momencie, a zatem funkcja nigdy nie może zostać wywołana. Obecna wersja underscore.js wydaje się mieć nieco inną kontrolę, gdzie oceniaimmediate && !timeout
przed wywołaniemsetTimeout
. (Algorytm też jest trochę inny, np. Nie używaclearTimeout
.) Dlatego zawsze powinieneś starać się używać najnowszej wersji swoich bibliotek. :-)źródło
!timeout
co sprawdzać na końcu? Dlaczego nie istnieje zawsze (ponieważ jest ustawiony nasetTimeout(function() etc.)
debounce
, tak, ale jest współdzielona przez wywołania funkcji zwracanej (która jest funkcją, której będziesz używać). Na przykład wg = debounce(f, 100)
programie wartośćtimeout
utrzymuje się w przypadku wielu wywołań funkcjig
.!timeout
Czek na końcu jest błędem wierzę, i nie jest w obecnym kodem underscore.js.null
. W moich testach z powyższym kodem ustawienie natychmiastowe na true powoduje, że funkcja w ogóle nie wywołuje, jak wspomniałeś. Jakiekolwiek rozwiązanie bez podkreślenia?Wywołane funkcje nie są wykonywane po wywołaniu, przed wykonaniem czekają na pauzę wywołań przez konfigurowalny czas trwania; każde nowe wywołanie restartuje licznik czasu.
Ograniczone funkcje są wykonywane, a następnie czekają przez określony czas, zanim będą mogły ponownie uruchomić.
Odbicie świetnie nadaje się do wydarzeń związanych z naciśnięciem klawisza; kiedy użytkownik zacznie pisać, a następnie przerwie, wysyłasz wszystkie naciśnięcia klawiszy jako jedno zdarzenie, zmniejszając w ten sposób obsługę wywołań.
Throttle świetnie sprawdza się w przypadku punktów końcowych czasu rzeczywistego, w przypadku których użytkownik chce zezwolić użytkownikowi na wywoływanie tylko raz na określony czas.
Sprawdź także Underscore.js dla ich implementacji.
źródło
Napisałem post zatytułowany Demistifying Debounce in JavaScript, w którym wyjaśniam dokładnie, jak działa funkcja debounce i dołączam demonstrację.
Ja też nie do końca rozumiałem, jak działa funkcja odbicia, kiedy pierwszy raz ją spotkałem. Chociaż są stosunkowo małe, w rzeczywistości wykorzystują dość zaawansowane koncepcje JavaScript! Pomocne będzie dobre trzymanie się lunety, zamknięć i
setTimeout
metody.Powiedziawszy to, poniżej jest wyjaśniona i pokazana podstawowa funkcja debounce w moim poście, o którym mowa powyżej.
Skończony produkt
Wyjaśnienie
źródło
To, co chcesz zrobić, jest następujące: Jeśli próbujesz wywołać funkcję zaraz po drugiej, pierwsza powinna zostać anulowana, a nowa powinna poczekać na określony czas, a następnie wykonać. Więc w efekcie potrzebujesz jakiegoś sposobu na anulowanie limitu czasu pierwszej funkcji? Ale jak? Państwo mogłoby wywołać funkcję i przechodzą powracającego Timeout-id, a następnie przekazać ten identyfikator do nowych funkcji. Ale powyższe rozwiązanie jest o wiele bardziej eleganckie.
Skutecznie udostępnia
timeout
zmienną w zakresie zwracanej funkcji. Tak więc, gdy uruchamiane jest zdarzenie „resize”, nie jest ono wywoływanedebounce()
ponownie, dlategotimeout
zawartość nie jest zmieniana (!) I nadal jest dostępna dla „następnego wywołania funkcji”.Kluczową rzeczą jest to, że wywołujemy funkcję wewnętrzną za każdym razem, gdy mamy zdarzenie zmiany rozmiaru. Być może będzie to bardziej jasne, jeśli wyobrazimy sobie, że wszystkie zdarzenia zmiany rozmiaru są w tablicy:
Widzisz, że
timeout
jest dostępny do następnej iteracji? Moim zdaniem nie ma powodu, aby zmieniać nazwęthis
nacontent
iarguments
naargs
.źródło
this
iarguments
zmienia się wewnątrz funkcji wywołania zwrotnego setTimeout (). Musisz zachować kopię w innym miejscu lub ta informacja zostanie utracona.Jest to odmiana, która zawsze uruchamia zdemontowaną funkcję przy pierwszym wywołaniu, z bardziej opisowymi nazwami zmiennych:
źródło
Prosta metoda Debounce w javascript
Przykład wykonania JSFiddle: https://jsfiddle.net/arbaazshaikh919/d7543wqe/10/
źródło
Prosta funkcja odbicia: -
HTML: -
JavaScript: -
źródło