Je suis intéressé par la fonction "debouncing" en javascript, écrite ici: http://davidwalsh.name/javascript-debounce-function
Malheureusement, le code n'est pas expliqué assez clairement pour que je le comprenne. Quelqu'un peut-il m'aider à comprendre comment cela fonctionne (j'ai laissé mes commentaires ci-dessous). Bref je ne comprends vraiment pas comment ça marche
// 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);
};
};
EDIT: L'extrait de code copié était auparavant callNow
au mauvais endroit.
javascript
debouncing
Startec
la source
la source
clearTimeout
avec quelque chose qui n'est pas un ID de minuterie valide, cela ne fait rien.WindowTimers
objet sur lequel la méthode a été invoquée, la méthode ne fait rien."Réponses:
Le code de la question a été légèrement modifié par rapport au code du lien. Dans le lien, il y a une vérification
(immediate && !timeout)
AVANT de créer un nouveau délai d'attente. L'avoir après fait que le mode immédiat ne se déclenche jamais. J'ai mis à jour ma réponse pour annoter la version de travail à partir du lien.la source
immediate && timeout
chèque. N'y aura-t-il pas toujours untimeout
(car iltimeout
est appelé plus tôt). Aussi, à quoi sertclearTimeout(timeout)
-il, quand il est déclaré (le rendant non défini) et effacé, plus tôtimmediate && !timeout
vérification concerne le moment où l'anti-rebond est configuré avec l'immediate
indicateur. Cela exécutera la fonction immédiatement mais imposera unwait
délai avant de pouvoir être exécuté à nouveau. Donc, la!timeout
partie dit essentiellement 'désolé bub, cela a déjà été exécuté dans la fenêtre définie' ... rappelez-vous que la fonction setTimeout l'effacera, permettant au prochain appel de s'exécuter.setTimeout
fonction? De plus, j'ai essayé ce code, pour moi, passertrue
pour immédiat empêche simplement la fonction d'être appelée du tout (plutôt que d'être appelée après un délai). Cela vous arrive-t-il?La chose importante à noter ici est que
debounce
produit une fonction qui est "fermée sur" latimeout
variable. Latimeout
variable reste accessible à chaque appel de la fonction produite même aprèsdebounce
son retour, et peut changer sur différents appels.L'idée générale
debounce
est la suivante:Le premier point est juste
var timeout;
, c'est effectivement justeundefined
. Heureusement, ilclearTimeout
est assez laxiste sur son entrée: passer unundefined
identifiant de minuterie le fait simplement ne rien faire, il ne génère pas d'erreur ou quelque chose.Le deuxième point est fait par la fonction produite. Il stocke d'abord des informations sur l'appel (le
this
contexte et learguments
) dans des variables afin de pouvoir les utiliser ultérieurement pour l'appel déboncé. Il efface ensuite le délai (s'il y en avait un), puis en crée un nouveau pour le remplacer en utilisantsetTimeout
. Notez que cela écrase la valeur detimeout
et que cette valeur persiste sur plusieurs appels de fonction! Cela permet au anti-rebond de fonctionner réellement: si la fonction est appelée plusieurs fois, elletimeout
est écrasée plusieurs fois avec une nouvelle minuterie. Si ce n'était pas le cas, plusieurs appels provoqueraient le démarrage de plusieurs temporisateurs, qui resteraient tous actifs - les appels seraient simplement retardés, mais pas rejetés.Le troisième point est fait dans le rappel de timeout. Il annule la
timeout
variable et effectue l'appel de fonction réel en utilisant les informations d'appel stockées.Le
immediate
drapeau est censé contrôler si la fonction doit être appelée avant ou après le temporisateur. Si tel est le casfalse
, la fonction d'origine n'est appelée qu'après avoir frappé la minuterie. Si tel est le castrue
, la fonction d'origine est appelée en premier et ne sera plus appelée tant que la minuterie ne sera pas atteinte.Cependant, je pense que la
if (immediate && !timeout)
vérification est erronée:timeout
vient d'être définie sur l'identificateur de minuterie renvoyé parsetTimeout
donc!timeout
toujoursfalse
à ce point et donc la fonction ne peut jamais être appelée. La version actuelle de underscore.js semble avoir une vérification légèrement différente, où elle évalueimmediate && !timeout
avant d' appelersetTimeout
. (L'algorithme est également un peu différent, par exemple il ne l'utilise pasclearTimeout
.) C'est pourquoi vous devriez toujours essayer d'utiliser la dernière version de vos bibliothèques. :-)la source
!timeout
à la fin? Pourquoi n'existe-t-il pas toujours (car il est réglé sursetTimeout(function() etc.)
debounce
, oui, mais il est partagé entre les appels à la fonction retournée (qui est la fonction que vous allez utiliser). Par exemple, dansg = debounce(f, 100)
, la valeur detimeout
persiste sur plusieurs appels àg
. La!timeout
vérification à la fin est une erreur, je crois, et ce n'est pas dans le code underscore.js actuel.null
. Dans mes tests avec le code ci-dessus, la définition immédiate de true empêche la fonction d'appeler du tout, comme vous l'avez mentionné. Une solution sans trait de soulignement?Les fonctions rebondies ne s'exécutent pas lorsqu'elles sont appelées, elles attendent une pause des appels pendant une durée configurable avant de s'exécuter; chaque nouvel appel redémarre le minuteur.
Les fonctions limitées s'exécutent, puis attendent une durée configurable avant de pouvoir se déclencher à nouveau.
Le debounce est idéal pour les événements de pression de touche; lorsque l'utilisateur commence à taper puis s'arrête, vous soumettez toutes les pressions sur les touches en un seul événement, réduisant ainsi les appels de gestion.
Throttle est idéal pour les points de terminaison en temps réel que vous ne souhaitez autoriser l'utilisateur à appeler qu'une seule fois par période définie.
Découvrez également Underscore.js pour leurs implémentations.
la source
J'ai écrit un article intitulé Demistifying Debounce en JavaScript dans lequel j'explique exactement comment fonctionne une fonction anti-rebond et j'inclus une démo.
Je n'ai pas non plus pleinement compris comment fonctionnait une fonction anti-rebond lorsque j'en ai rencontré une pour la première fois. Bien que de taille relativement petite, ils utilisent en fait des concepts JavaScript assez avancés! Avoir une bonne prise sur la portée, les fermetures et la
setTimeout
méthode vous aidera.Cela dit, vous trouverez ci-dessous la fonction anti-rebond de base expliquée et démontrée dans mon article référencé ci-dessus.
Le produit fini
L'explication
la source
Ce que vous voulez faire est ce qui suit: Si vous essayez d'appeler une fonction juste après une autre, la première doit être annulée et la nouvelle doit attendre un délai d'attente donné, puis s'exécuter. Donc, en fait, vous avez besoin d'un moyen d'annuler le délai d'expiration de la première fonction? Mais comment? Vous pouvez appeler la fonction et transmettre le timeout-id de retour, puis passer cet ID dans toutes les nouvelles fonctions. Mais la solution ci-dessus est bien plus élégante.
Cela rend effectivement la
timeout
variable disponible dans la portée de la fonction retournée. Ainsi, quand un événement 'resize' est déclenché, il n'appelledebounce()
plus, donc letimeout
contenu n'est pas modifié (!) Et toujours disponible pour le "prochain appel de fonction".L'essentiel ici est que nous appelons la fonction interne chaque fois que nous avons un événement de redimensionnement. C'est peut-être plus clair si nous imaginons que tous les événements de redimensionnement sont dans un tableau:
Vous voyez que le
timeout
est disponible pour la prochaine itération? Et il n'y a aucune raison, à mon avis, de renommerthis
encontent
etarguments
enargs
.la source
this
et lesarguments
changements dans la fonction de rappel setTimeout (). Vous devez conserver une copie ailleurs ou cette information est perdue.Il s'agit d'une variante qui déclenche toujours la fonction non rebondie la première fois qu'elle est appelée, avec des variables nommées de manière plus descriptive:
la source
Méthode simple Debounce en javascript
Exemple d'exécution JSFiddle: https://jsfiddle.net/arbaazshaikh919/d7543wqe/10/
la source
Fonction anti-rebond simple: -
HTML: -
Javascript: -
la source