javascript underscore.js

javascript - ¿Qué hace_.debounce?



underscore.js (4)

Básicamente, acelera las llamadas, por lo que si se llama más de una vez en un corto período de tiempo, solo se llamará a una instancia.

¿Por qué lo usarías?

Eventos como window.onresize fire varias veces en rápida sucesión. Si necesita hacer muchos cálculos en la nueva posición, no querrá disparar los cálculos varias veces. Solo desea iniciarlo cuando el usuario haya terminado el cambio de tamaño.

Un proyecto en el que he estado trabajando utiliza _.debounce ().

La documentación de Subderscore JS para rebote se lee de la siguiente manera:

rebote _.debounce(function, wait, [immediate])

Crea y devuelve una nueva versión depurada de la función pasada que pospondrá su ejecución hasta que hayan transcurrido los milisegundos de espera desde la última vez que se invocó.

Esto obviamente supone que cualquiera que quiera saber qué debounce() hace, ya sabe lo que significa ''rebote''.

¿Qué hace realmente reblandecimiento?


Descripción del código fuente de underscore.js:

Devuelve una función que, mientras siga siendo invocada, no se activará. Se llamará a la función después de que deje de llamarse durante N milisegundos. Si se pasa ''inmediato'', active la función en el borde delantero, en lugar del final.

Codifíquese a sí mismo:

_.debounce = function(func, wait, immediate) { var timeout, result; return function() { var context = this, args = arguments; var later = function() { timeout = null; if (!immediate) result = func.apply(context, args); }; var callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) result = func.apply(context, args); return result; }; };


Escribí una publicación titulada Demystifying Debounce en JavaScript donde explico exactamente cómo funciona una función antirrebote e incluyo una demostración.

Una función antirrebote proporciona una forma de "acelerar" la ejecución de una función. Normalmente se usan en circunstancias en las que una función está vinculada a un evento que se dispara en rápida sucesión. Es común ver que se usa una función antirrebote en el tamaño y el desplazamiento de la ventana.

Ya sea Underscores u otra biblioteca de JavaScript, todas las funciones de rebote se basan en el método nativo setTimeout JavaScript. Entonces, antes de sumergirse en la comprensión de lo que hace una función antirrebote, es una buena idea tener una comprensión completa de WindowTimers (enlaces a MDN).

Además, querrá tener una buena comprensión del alcance y los cierres. Aunque es relativamente pequeño en tamaño, ¡las funciones antirrebote realmente emplean algunos conceptos bastante avanzados de JavaScript!

Dicho esto, a continuación se explica y muestra la función básica de eliminación del rebote en mi publicación mencionada anteriormente.

El producto terminado

// Create JD Object // ---------------- var JD = {}; // Debounce Method // --------------- JD.debounce = function(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 || 200); if ( callNow ) { func.apply(context, args); } }; };

La explicación

// Create JD Object // ---------------- /* It''s a good idea to attach helper methods like `debounce` to your own custom object. That way, you don''t pollute the global space by attaching methods to the `window` object and potentially run in to conflicts. */ var JD = {}; // Debounce Method // --------------- /* Return 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 `wait` milliseconds. If `immediate` is passed, trigger the function on the leading edge, instead of the trailing. */ JD.debounce = function(func, wait, immediate) { /* Declare a variable named `timeout` variable that we will later use to store the *timeout ID returned by the `setTimeout` function. *When setTimeout is called, it retuns a numeric ID. This unique ID can be used in conjunction with JavaScript''s `clearTimeout` method to prevent the code passed in the first argument of the `setTimout` function from being called. Note, this prevention will only occur if `clearTimeout` is called before the specified number of milliseconds passed in the second argument of setTimeout have been met. */ var timeout; /* Return an anomymous function that has access to the `func` argument of our `debounce` method through the process of closure. */ return function() { /* 1) Assign `this` to a variable named `context` so that the `func` argument passed to our `debounce` method can be called in the proper context. 2) Assign all *arugments passed in the `func` argument of our `debounce` method to a variable named `args`. *JavaScript natively makes all arguments passed to a function accessible inside of the function in an array-like variable named `arguments`. Assinging `arguments` to `args` combines all arguments passed in the `func` argument of our `debounce` method in a single variable. */ var context = this, /* 1 */ args = arguments; /* 2 */ /* Assign an anonymous function to a variable named `later`. This function will be passed in the first argument of the `setTimeout` function below. */ var later = function() { /* When the `later` function is called, remove the numeric ID that was assigned to it by the `setTimeout` function. Note, by the time the `later` function is called, the `setTimeout` function will have returned a numeric ID to the `timeout` variable. That numeric ID is removed by assiging `null` to `timeout`. */ timeout = null; /* If the boolean value passed in the `immediate` argument of our `debouce` method is falsy, then invoke the function passed in the `func` argument of our `debouce` method using JavaScript''s *`apply` method. *The `apply` method allows you to call a function in an explicit context. The first argument defines what `this` should be. The second argument is passed as an array containing all the arguments that should be passed to `func` when it is called. Previously, we assigned `this` to the `context` variable, and we assigned all arguments passed in `func` to the `args` variable. */ if ( !immediate ) { func.apply(context, args); } }; /* If the value passed in the `immediate` argument of our `debounce` method is truthy and the value assigned to `timeout` is falsy, then assign `true` to the `callNow` variable. Otherwise, assign `false` to the `callNow` variable. */ var callNow = immediate && !timeout; /* As long as the event that our `debounce` method is bound to is still firing within the `wait` period, remove the numerical ID (returned to the `timeout` vaiable by `setTimeout`) from JavaScript''s execution queue. This prevents the function passed in the `setTimeout` function from being invoked. Remember, the `debounce` method is intended for use on events that rapidly fire, ie: a window resize or scroll. The *first* time the event fires, the `timeout` variable has been declared, but no value has been assigned to it - it is `undefined`. Therefore, nothing is removed from JavaScript''s execution queue because nothing has been placed in the queue - there is nothing to clear. Below, the `timeout` variable is assigned the numerical ID returned by the `setTimeout` function. So long as *subsequent* events are fired before the `wait` is met, `timeout` will be cleared, resulting in the function passed in the `setTimeout` function being removed from the execution queue. As soon as the `wait` is met, the function passed in the `setTimeout` function will execute. */ clearTimeout(timeout); /* Assign a `setTimout` function to the `timeout` variable we previously declared. Pass the function assigned to the `later` variable to the `setTimeout` function, along with the numerical value assigned to the `wait` argument in our `debounce` method. If no value is passed to the `wait` argument in our `debounce` method, pass a value of 200 milliseconds to the `setTimeout` function. */ timeout = setTimeout(later, wait || 200); /* Typically, you want the function passed in the `func` argument of our `debounce` method to execute once *after* the `wait` period has been met for the event that our `debounce` method is bound to (the trailing side). However, if you want the function to execute once *before* the event has finished (on the leading side), you can pass `true` in the `immediate` argument of our `debounce` method. If `true` is passed in the `immediate` argument of our `debounce` method, the value assigned to the `callNow` variable declared above will be `true` only after the *first* time the event that our `debounce` method is bound to has fired. After the first time the event is fired, the `timeout` variable will contain a falsey value. Therfore, the result of the expression that gets assigned to the `callNow` variable is `true` and the function passed in the `func` argument of our `debounce` method is exected in the line of code below. Every subsequent time the event that our `debounce` method is bound to fires within the `wait` period, the `timeout` variable holds the numerical ID returned from the `setTimout` function assigned to it when the previous event was fired, and the `debounce` method was executed. This means that for all subsequent events within the `wait` period, the `timeout` variable holds a truthy value, and the result of the expression that gets assigned to the `callNow` variable is `false`. Therefore, the function passed in the `func` argument of our `debounce` method will not be executed. Lastly, when the `wait` period is met and the `later` function that is passed in the `setTimeout` function executes, the result is that it just assigns `null` to the `timeout` variable. The `func` argument passed in our `debounce` method will not be executed because the `if` condition inside the `later` function fails. */ if ( callNow ) { func.apply(context, args); } }; };


Mantiene la ejecución de una función hasta que expira el tiempo de espera. Esto es para evitar la ejecución continua de funciones cuando no es necesario. Tenga cuidado, ya que underscore.debounce () se basa en código complejo. La mayoría de las veces, una simple declaración "si" dentro de la función es mucho más rápida que rebotar. Puede implementar un contador, ejecutando el método solo cada N de iteraciones o un tiempo de espera, verificando que haya transcurrido al menos cierta cantidad de milisegundos.