Décorateur d'accélération
Créez un décorateur âdâaccélérationâ throttle(f, ms) â qui retourne un wrapper.
Lorsquâil est appelé plusieurs fois, il passe lâappel à f au maximum une fois par ms millisecondes.
La différence avec debounce est que câest un décorateur complètement différent :
debounceexécute la fonction une fois après la période de âcooldownâ. Bon pour traiter le résultat final.throttlene lâexécute pas plus souvent que le temps donné enms. Bon pour les mises à jour régulières qui ne devraient pas être très fréquentes.
En dâautres termes, throttle est comme une secrétaire qui accepte les appels téléphoniques, mais qui dérange le patron (appellez le f réel) pas plus dâune fois par ms millisecondes.
Examinons lâapplication réelle pour mieux comprendre cette exigence et voir dâoù elle vient.
Par exemple, nous voulons suivre les mouvements de la souris.
Dans le navigateur, nous pouvons configurer une fonction à exécuter à chaque mouvement de la souris et obtenir lâemplacement du pointeur à mesure quâil se déplace. Pendant une utilisation active de la souris, cette fonction est généralement utilisée très souvent et peut atteindre 100 fois par seconde (toutes les 10 ms). Nous aimerions mettre à jour certaines informations sur la page Web lorsque le pointeur se déplace.
⦠Mais la mise à jour de la fonction update() est trop lourde pour tous les micro-mouvements. Il est également inutile de mettre à jour plus dâune fois toutes les 100 ms.
Nous allons donc lâenvelopper dans le décorateur : utilisez throttle(update, 100) comme fonction à exécuter à chaque déplacement de souris à la place de update() dâorigine. Le décorateur sera appelé souvent, mais update() sera appelé au maximum une fois toutes les 100 ms.
Visuellement, cela ressemblera à ceci:
- Pour le premier mouvement de souris, la variante décorée passe lâappel Ã
update. Cela est important, lâutilisateur voit notre réaction à leur mouvement immédiatement. - Puis, alors que la souris continue dâavancer, il ne se passe plus rien jusquâÃ
100ms. La variante décorée ignore les appels. - à la fin de
100msâ une autreupdatese produit avec les dernières coordonnées. - Enfin, la souris sâarrête quelque part. La variante décorée attend que
100msexpire, puis lanceupdateavec les dernières coordonnées. Donc, peut-être le plus important, les coordonnées finales de la souris sont traitées.
Un exemple de code:
function f(a) {
console.log(a);
}
// f1000 passe les appels à f au maximum une fois toutes les 1000 ms
let f1000 = throttle(f, 1000);
f1000(1); // montre 1
f1000(2); // (étranglement, 1000ms pas encore écoulée)
f1000(3); // (étranglement, 1000ms pas encore écoulée)
// quand 1000ms expirent...
// ...sort 3, la valeur intermédiaire 2 a été ignorée
P.S. Les arguments et le contexte this transmis à f1000 doivent être transmis à f dâorigine.
function throttle(func, ms) {
let isThrottled = false,
savedArgs,
savedThis;
function wrapper() {
if (isThrottled) { // (2)
savedArgs = arguments;
savedThis = this;
return;
}
isThrottled = true;
func.apply(this, arguments); // (1)
setTimeout(function() {
isThrottled = false; // (3)
if (savedArgs) {
wrapper.apply(savedThis, savedArgs);
savedArgs = savedThis = null;
}
}, ms);
}
return wrapper;
}
Un appel à throttle(func, ms) retourne wrapper.
- Lors du premier appel, le
wrapperexécute simplementfuncet définit lâétat de charge (isThrottled = true). - Dans cet état, tous les appels sont mémorisés dans
savedArgs/savedThis. Veuillez noter que le contexte et les arguments sont dâégale importance et doivent être mémorisés. Nous avons besoin dâeux simultanément pour reproduire lâappel. - Après
msmillisecondes,setTimeoutse déclenche. Lâétat de charge est supprimé (isThrottled = false), et si nous avions ignoré les appels, alorswrapperest exécuté avec les derniers arguments et contextes mémorisés.
La 3ème étape nâexécute pas func, mais wrapper, car nous devons non seulement exécuter func, mais encore une fois entrer dans lâétat de charge et configurer le délai dâexpiration pour le réinitialiser.