Si j'utilise _.debounce() pour créer une fonction anti-rebond et que je l'appelle 3 fois de suite avec 3 ensembles d'arguments différents, alors (à partir de la v1.1.7) la fonction de charge utile enveloppée sera finalement appelée avec les arguments spécifiés par le 3e appel - c'est-à-dire que les premier et deuxième arguments sont ignorés.
Bien que cela soit souvent valide (et ce que fait généralement l'anti-rebond des clés, donc une valeur par défaut raisonnable), je me retrouve à vouloir utiliser l'anti-rebond pour accumuler des arguments, par exemple j'ai un appel AJAX qui peut obtenir plusieurs clés à la fois, donc j'utilise l'anti-rebond pour mettre en mémoire tampon clés pendant une seconde, puis émettez une demande combinée.
Ma suggestion est donc que debounce prend un 3ème argument optionnel "combine" qui sera appelé avec 2 args,
Si aucune valeur n'est transmise pour le paramètre de combinaison, la combinaison par défaut préserve le comportement existant
function(acc, newargs) { return newargs; }
mais vous pouvez également décider d'utiliser le premier ensemble d'arguments
function(acc, newargs) { return acc || newargs; }
ou ce que je veux faire, c'est-à-dire simplement ajouter tous les arguments
function(acc,newargs) { return (acc || []).concat(newargs); }
et bien sûr d'autres voudront peut-être faire quelque chose de plus fantaisiste
Cela nécessiterait la modification suivante de la fonction de limite interne
// Internal function used to implement `_.throttle` and `_.debounce`.
var limit = function(func, wait, debounce, combine) {
var timeout, allargs;
return function() {
var context = this;
allargs = combine(allargs, slice.call(arguments,0))
var throttler = function() {
timeout = null;
var args = allargs;
allargs = undefined;
func.apply(context, args);
};
if (debounce) clearTimeout(timeout);
if (debounce || !timeout) timeout = setTimeout(throttler, wait);
};
};
puis une modification de debounce pour accepter et transmettre le nouvel argument avec la valeur par défaut s'il n'est pas spécifié.
_.debounce = function(func, wait, combine) {
return limit(func, wait, true, combine || function(acc,newargs) { return newargs; });
};
La fonction de régulation correspondante utilise actuellement le premier ensemble d'arguments seul (la régulation ignore efficacement les appels qui se produisent dans les millisecondes d'attente d'un premier appel et utilise le premier ensemble d'arguments d'appel, l'anti-rebond ignore effectivement tous les appels sauf le dernier dans une séquence se produisant pendant la période d'attente les uns des autres), je suggérerais donc ce qui suit pour préserver à nouveau le comportement par défaut actuel
_.throttle = function(func, wait, combine) {
return limit(func, wait, false, combine || function(acc,newargs) { return acc || newargs; });
};
Cela semblerait le moyen le plus simple et le plus général d'obtenir cette fonctionnalité sans emballages excessifs pour maintenir les listes d'arguments, mais je serais intéressé de savoir s'il existe un moyen simple d'y parvenir sans changer le trait de soulignement.
Commentant ma propre suggestion, l'appel à combine() doit spécifier le même contexte que la fonction de charge utile, d'où
allargs = combine.apply(this, [allargs, slice.call(arguments,0)])
au cas où les arguments auraient besoin d'accéder à l'objet de contexte...
Devrait maintenant être fixé sur le maître. throttle
devrait afficher le comportement correct où il utilise toujours la dernière copie de vos arguments, se déclenche une fois immédiatement, et toutes les N
secondes ensuite ... et se réinitialise N
secondes après le dernier déclencheur final s'est produit.
Je pense que votre commentaire proche s'applique à un autre problème (probablement # 170), car le problème soulevé par cette demande s'applique toujours au maître.
Il n'y a toujours pas de moyen facile de faire en sorte que les arguments anti-rebond ou étranglement accumulent les arguments des appels qui sont combinés, et je pense toujours que c'est un ajout facultatif utile qui laisse le comportement par défaut inchangé lorsque l'argument de combinaison facultatif n'est pas spécifié.
Ah, tu as raison. L'accumulation d'arguments sort du cadre de Underscore - n'hésitez pas à stocker vos données accumulées dans un bon endroit en dehors des fonctions _.throttle
et _.debounce
.
C'est dommage, je considère que le debounce est une sorte de fold-left (reduce) sur plusieurs appels avec timeout d'où l'accumulateur... mais c'est votre appel :)
OK, ne pas continuer à taper dessus, mais au cas où quelqu'un regarderait cela un peu plus tard et se demanderait comment faire la même chose, j'ai pensé que c'était à peu près la manière la plus propre sans modifier le debounce lui-même (je l'ajoute à l'objet _ , d'autres peuvent préférer ne pas pour)
_.mixin({
debounceReduce: function(func, wait, combine) {
var allargs,
context,
wrapper = _.debounce(function() {
var args = allargs;
allargs = undefined;
func.apply(context, args);
}, wait);
return function() {
context = this;
allargs = combine.apply(context, [allargs, Array.prototype.slice.call(arguments,0)]);
wrapper();
};
}
})
Cela donne une fonction anti-rebond dont les arguments sont réduits par la fonction de combinaison, par exemple,
delayLog = _.debounceReduce(function() { console.log(arguments); }, 5000,
function(acc,args) { return (acc || []).concat(args); });
delayLog(3,4);
delayLog(7,8,9);
appellera quelques secondes plus tard console.log avec le tableau [3,4,7,8,9]
@schmerg - cela semble extrêmement utile. Seriez-vous prêt à licencier ce code sous la licence MIT ? (Un "oui" suffira !)
@markjaquith Bien sûr - oui. Heureux de...
Si quelqu'un vient et veut une version js moderne mise à jour/commentée de ce qui précède :
_.mixin({
debounceReduce(func, wait, combine) {
let allArgs; // accumulator for args across calls
// normally-debounced fn that we will call later with the accumulated args
const wrapper = _.debounce(() => func(allArgs), wait);
// what we actually return is this function which will really just add the new args to
// allArgs using the combine fn
return (...args) => {
allArgs = combine(allArgs, [...args]);
wrapper();
};
},
});
@kmannislands Hé, votre version ne réinitialise pas allArgs
à l'intérieur wrapper()
, donc les appels ultérieurs à func()
obtiennent des lots historiques d'arguments ainsi que le lot actuel.
Ne devrait-il pas être :
const wrapper = _.debounce(() => {
const args = allArgs;
allArgs = undefined;
func(args);
}, wait);
@markjaquith +1
De plus, func( args )
diffère de la version originale qui utilise func.apply(context, args);
.
Pour le premier, args
est utilisé tel quel dans la cible func()
, alors que dans le dernier _(code original)_ vous devez utiliser soit arguments
dans une fonction normale ou ( ...args )
dans une fonction de flèche grasse es6.
Commentaire le plus utile
OK, ne pas continuer à taper dessus, mais au cas où quelqu'un regarderait cela un peu plus tard et se demanderait comment faire la même chose, j'ai pensé que c'était à peu près la manière la plus propre sans modifier le debounce lui-même (je l'ajoute à l'objet _ , d'autres peuvent préférer ne pas pour)
Cela donne une fonction anti-rebond dont les arguments sont réduits par la fonction de combinaison, par exemple,
appellera quelques secondes plus tard console.log avec le tableau [3,4,7,8,9]