Cómo sustituir GIFs animados por vídeo y ganar en el proceso

29/08/2018
Artículo original

Seamos realistas: los GIF son un vestigio del pasado. Nacieron en los años oscuros de la prehistoria de Internet (siglo pasado) cuando reproducir vídeo en un navegador no era más que un sueño. Este formato permite únicamente secuencias de imágenes de 256 colores, con muy poca calidad. Pero este no es su mayor problema. El verdadero handicap que presentan es que ocupan un montón. Vete por ejemplo a Giphy y elige un GIF animado cualquiera que tenga imágenes reales. Si miras su información, verás que ocupan una barbaridad:

En el ejemplo del vídeo anterior, a pesar de ser tan aparentemente simple, con fondo neutro y todo, la animación ocupa casi 4MB.

Lo increíble es que este formato vuelve a estar de moda. De hecho, está más presente que nunca y vemos GIFs animados en casi todas partes: Twitter, programas móviles de mensajería, aplicaciones de chat... Se han convertido casi en un lenguaje de comunicación propio.

Sin embargo no todo lo que se llama GIF es un GIF en realidad.

Por ejemplo, en Giphy las animaciones que se muestran son realmente vídeos aunque en origen se tratase de un gráfico en formato .gif. Y si subes un GIF animado a Twitter verás que la aplicación lo convierte en MP4 por detrás y es lo que se usa. ¿Por qué? Pues porque el ancho de banda cuesta dinero y usar esas atrocidades directamente sería gastar mucho más. Además, para los usuarios no es lo mismo tampoco descargar un archivo de varios megas que uno mucho más pequeño. Cuanto más pequeño, antes descarga y mejor parece funcionar la página. Finalmente, el reproducir un GIF es más costoso para el navegador que reproducir el vídeo equivalente, que consume menos CPU y, en algunos casos, puede incuso hacer uso directo de la GPU para decodificar vídeo MP4.

En definitiva: usar GIFs directamente no se justifica más que en algunos pocos casos.

¿En qué casos? Pues por ejemplo si las animaciones representan acciones cortas en una pantalla. Sin ir más lejos, yo mismo cuando tengo que mostrar algo rápido (unos pocos segundos) en algún programa, grabando mi pantalla, utilizo el formato GIF, optimizándolo. El motivo es que las interfaces de la mayor parte de los programas de ordenador usan muy pocos colores (por regla general llega con 16) y además usan bordes muy bien definidos: justo lo que va bien con GIF y mal con vídeo muy comprimido. Si usas el programa adecuado en muchos casos puede ser más efectivo el GIF que un vídeo. Pero en la mayor parte de los casos, no será así. El mejor programa que conozco es para Windows y se llama ScreenToGif. Graba genial, te deja editar y optimiza las animaciones mucho. Y encima es gratis, pero ¡no dejes de donarles si lo usas mucho!

¿Cómo sustituir GIFs con vídeos de la manera óptima?

En la mayor parte de los casos, como digo, es más recomendable utilizar un vídeo que un GIF, ya que ocupará mucho menos. Pero la gracia de los GIF es que se reproducen automáticamente y entran en bucle, cosa que, por defecto, los vídeos no hacen ¿cierto?

La buena noticia es que es muy fácil emular este comportamiento con un vídeo en HTML5. Vamos a verlo.

1.- Convertir el GIF a vídeo MP4

Lo primero es obtener una versión en vídeo del GIF original.

Si es una animación de Giphy es algo directo, ya que en el apartado "Media" te ofrece directamente el enlace directo al vídeo:

En el caso del vídeo de la chica danzarina del ejemplo, el MP4 de la animación ocupa tan solo 334KB. Esto es un orden de magnitud menos que el GIF original. Será el que utilicemos.

En caso de que tengas acceso al archivo .gif original y necesites convertirlo a .mp4 tienes varias opciones. El programa Screen2Gif que mencioné arriba tiene esta capacidad pero necesitarás instalar la biblioteca ffmpeg, que entre otras cosas instala los drivers necesarios para hacer esta conversión. De hecho puedes usar directamente ffmpeg para hacer la conversión en cualquier plataforma (Windows, Mac o Linux) ya que funciona en todas ellas. Tan solo has de lanzar este comando desde un terminal:

ffmpeg -i miAnimacion.gif miAnimacion.mp4

Esta es la conversión básica, pero ffmpeg tiene cantidad de parámetros que puedes configurar, así que si quieres investigar en la documentación puedes hacer que comprima mucho más todavía que lo que lo hace por defecto, y dependiendo del vídeo le irán mejor unos parámetros u otros.

Yo, de todos modos, para no complicarme la vida muchas veces suelo utilizar el servicio online de conversión de archivos CloudConvert, que es capaz de convertir casi cualquier cosa que le eches, y además tiene una capa gratuita generosa que para conversiones esporádicas te saldrá gratis (si haces conversiones más a menudo es muy barato y funciona genial: totalmente recomendado).

El formato MP4-H264 está soportado por todos los navegadores del mercado. En ese enlace te explica cómo funciona y qué capacidades tiene.

Existen otros formatos más modernos, como WebM, auspiciado por Google que comprime aún un poco más que MP4 y está soportado por los principales navegadores (Chrome, Opera y Firefox), pero que todavía puede darte problemas con navegadores más antiguos o con Safari, que en Mac necesita usar Quicktime con un plugin para darle soporte. Hoy por hoy puede ser una segunda fuente de vídeo tras la principal en MP4, pero yo no me casaría con él del todo, utilizando MP4 como formato universal.

2.- Incluir el vídeo en la página de la manera adecuada

Para que nuestro vídeo sea totalmente equivalente a un GIF convencional tiene que cumplir tres cosas:

  1. Debe reproducirse automáticamente en cuanto cargue la página
  2. Debe reproducirse de forma continua, en bucle
  3. No se puede parar ni controlar, ni tampoco poner a pantalla completa.

En realidad hay una cuarta condición, pero que es consecuencia de la primera: no puede reproducir audio. Y digo que es consecuencia de la primera porque para que un vídeo pueda reproducirse de manera automática los navegadores modernos obligan a que sea de manera silenciosa, para no molestar a los usuarios. Por ello para que se cumpla la primera condición el vídeo debe estar mudo. Esto no es un problema ya que los GIF no soportan audio, así que en cualquier caso si queremos simularlo no deben tener sonidos.

Para conseguir esto con una etiqueta <video> de HTML5 lo único que tenemos que poner es esto:

<video muted autoplay loop playsinline></video>

Del nombre de los atributos es fácil deducir para qué sirven:

  • muted: hace que, aunque el vídeo tuviera audio, no se reproduzca. Es necesario ponerlo o el navegador no lo reproducirá automáticamente.
  • autoplay: en cuanto el vídeo se carga, se empieza a reproducir. Solo funciona si el anterior está presente.
  • loop: ejecuta el vídeo en bucle, empezando de nuevo cuando llega al final. Igualito que un GIF.
  • playsinline: le indica al navegador que queremos que este vídeo se reproduzca siempre en la página, es decir, que no se pueda poner a pantalla completa.

Fíjate en que al no tener el atributo controls puesto lo que se consigue es que no se muestren los controles de reproducción, que están desactivados por defecto salvo que se le ponga este atributo.

Vale, solo queda ponerle la fuente de vídeo, que en este caso será la dirección pública en Internet en la que hayamos "colgado" el vídeo, para lo cual se usa el atributo src:

<video muted autoplay loop playsinline src="https://www.miservidor.com/media/miVideo.mp4">
</video>

En nuestro caso, el vídeo equivalente al GIF queda de la siguiente manera:

¿A que no se nota que es un vídeo en vez de un GIF?

La única forma de verlo (y de paso saltarse las restricciones) es pulsar con el botón secundario del ratón sobre éste, que muestra un menú que nos permite pararlo, activar los controles, etc...:

¡Espero que te resulte útil!

El developer feliz: qué buscamos y qué necesitamos

26/08/2018
Artículo original

Developer Feliz

Cuando preparaba “El Feliz Developer”, la charla para T3chFest que me animó a preparar un estudio sobre motivaciones profesionales en el sector IT, una de las cuestiones fundamentales en la investigación, era definir las necesidades más importantes de los trabajadores. El objetivo era reflexionar sobre qué cosas son las que buscamos, las que necesitamos, en nuestra vida y, sobre todo, también en el trabajo.

Hoy intentaremos analizar precisamente cuáles son nuestras necesidades, y por qué algunas son más importantes que otras dentro del entorno profesional.

¿Qué es lo que de verdad queremos?

Lo ideal antes de empezar a leer este artículo es que te sientes con una hoja de papel al lado. ¿Ya la tienes? Escribe en un lado cuáles son los factores que hacen que te sientas feliz en tu trabajo. Escribe en otro lado cuáles son los factores que podrían hacer que fueras infeliz en tu trabajo

Genial, ahora puede seguir leyendo. Pensar en este tipo de cosas sin conocer las clasificaciones teóricas, te permite ser mucho más sincero contigo mismo.

Los profesionales que respondieron a la encuesta de El Feliz Developer” dijeron que lo más valorado en su trabajo era, en este orden:

  • flexibilidad
  • salario,
  • buen ambiente
  • uso de las tecnologías más actuales.

Por otro lado, lo que más se echaba en falta era:

  • formación
  • salario acorde a las expectativas
  • uso de metodologías ágiles

La teoría de Herzberg: necesario, pero no suficiente para hacernos felices

¿Hasta qué punto piensas que tener una buena silla, la temperatura de la oficina, o una luz que no parpadee sobre ti te hace feliz?

Hay dos teorías, con sus matizaciones, claro, que nos ayudan a explicar muy bien cuáles son las necesidades y motivaciones que tenemos en el ambiente profesional.

Una de estas teorías es la de Herzberg, que analiza qué factores generan satisfacción, y qué factores generan insatisfacción. Lo realmente interesante de esta teoría, es que hay factores que ayudan a aumentar nuestra satisfacción, pero tienen muy poco efecto sobre la insatisfacción, y viceversa punto hay factores que cuando faltan causan insatisfacción, pero que estén presentes no garantiza la satisfacción de los trabajadores: son necesarios, pero no suficientes.

¿Hasta qué punto piensas que tener una buena silla, la temperatura de la oficina, o una luz que no parpadee sobre ti te hace feliz? No es algo que añadiríamos como algo que nos motivaría, pero sin embargo, si hace demasiado frío o calor, hay olor desagradable o ningún ascensor funciona, nos sentiriamos muy insatisfechos. Este tipo de reflexiones nos ayuda a valorar y entender la importancia de las “pequeñas” grandes cosas , y a comprender por qué desde la empresa es importante cuidar estos detalles, que muchas veces damos por supuesto.

¿Eres capaz de definir cuáles son los factores que deberían cumplirse, obligatorios si cambiaras de trabajo?

Maslow y la jerarquía de nuestras necesidades

Ahora, ponte delante de tu hoja de papel. ¿Eres capaz de definir cuáles son los factores que deberían cumplirse, obligatorios si cambiaras de trabajo? ¿Puedes definir por otro lado cuáles supondría un plus, pero son menos importantes para ti?

Muchos ya conoceréis la Pirámide de Maslow. Es nuestra segunda teoría: fue propuesta por Maslow en los años 40 que trata de jerarquizar las necesidades del ser humano: las ordena según su orden de prioridad. Estas necesidades están orientadas a la vida en general, y se han interpretado varias veces por diferentes autores.

Estas necesidades son básicas (las que nos permiten mantenernos vivos), seguridad, afiliación (como nos relacionamos) , reconocimiento (como nos perciben). Pueden trasladarse al entorno profesional, como vemos en la pirámide.

Piramide de Maslow adaptada al trabajo

Desde luego, y como podremos imaginar, este orden de necesidades puede cambiar: dependiendo de nuestra situación, valoramos más unas cosas que otras (después de una mala experiencia en un ambiente conflictivo, primaremos las necesidades de socialización o afiliación, si estamos en un momento en el que es muy necesario el crecimiento profesional por sentirnos encasillados, serán fundamentales las opciones de promoción al buscar un nuevo empleo.

Por ejemplo, a partir de cierta banda salarial. ¿Hay una diferencia real entre cobrar 80 u 85k al año?, el dinero tiene menos importancia en detrimento del tipo de trabajo, el clima laboral…. pero esto solo ocurre cuando ciertas bases están cubiertas

Es importante entender que las necesidades han de cubrirse primero desde la base e ir subiendo: no podemos hablar a alguien de que le vamos a dar entradas para ir al cines si le estamos pagando un salario ínfimo o no le damos herramientas mínimas para trabajar.

Por supuesto, en muchos casos, un problema social de afiliación puede convertirse en un problema de base: machismo, acoso, si afecta a la seguridad...

Cambiamos lo que somos, lo que necesitamos… y cómo influimos

Como curiosidad: fíjate en nuestra pirámide: las bases, en azul (básicas y seguridad) deben ser aseguradas por la empresa, es su responsabilidad. La parte verde es responsabilidad repartida, entre la empresa y nosotros (proceso de selección, relaciones informales) La cúspide depende más de nuestra propia percepción.

Y, una última observación. Vuelve a pensar en esos factores que hacen que te sientas bien en el trabajo. En tu día a día: la limpieza la seguridad… piensa en el trabajo de las personas que se esfuerzan por que tu base de la pirámide sea lo mejor posible. Quienes reparan tu ascensor, vacían tus papeleras, vigilan los accesos y se encargan de que el baño esté limpio. También tienen su propia pirámide, sus necesidades y sus motivaciones.

Éste puede ser también un buen ejercicio autoconocimiento. ¿Cuáles son los factores que has considerado necesarios (imprescindibles para un cambio)? ¿cuáles son los que has considerado motivadores (un plus)? ¿Qué puedes exigir a la organización para que tu pirámide mejore? ¿Cómo puedes afectar tú a la pirámide de los demás? ¿Qué soluciones de la pirámide aporta tu trabajo, y tu actitud?

Este tipo de reflexiones nos ayudan a conocer mejor cómo somos, cuáles son las necesidades que ahora no tenemos cubiertas, y cómo hemos evolucionado a lo largo de los años, tanto nosotros como nuestras motivaciones.

Foto | Raimond Spekking Video | El feliz developer | T3chFest 2018

GAMBADAS: engañar al GPS para enviar a tu coche autónomo directo contra el tráfico

24/08/2018
Artículo original


Foto de NESA by Makers en Unsplash

Cada día millones de personas utilizan el sistema global de posicionamiento americano, GPS. Todos los móviles llevan un sensor GPS incorporado que recibe señales de su constelación de satélites y mide desfases para determinar con precisión pasmosa la ubicación actual. Muchos coches también los incorporan, y en el futuro próximo los coches autónomos estarán al orden del día, y también lo usarán para apoyarse en sus datos de cara a la navegación.

Unos investigadores de Virginia Tech, la Universidad China de Ciencias Electrónicas y Tecnología y Microsoft Research han creado la prueba de concepto de un aparato que, pegado debajo de un coche puede engañar a los GPS cercanos para obligarles a cambiar la ruta y enviarte a otro lado o, directamente, meterte en una dirección prohibida en contra del tráfico, por ejemplo. El dispositivo está construido con Raspberry Pi, un dispositivo de radio que se puede definir con software llamado HackRF One, una antena y una batería. Invirtieron en total, debido al coste del HackRF One: 223 dólares (unos 190 euros).

El aparato se pega en los bajos de un coche que queremos atacar, que anula las señales del sistema GPS y emite unas propias simuladas que engañan al dispositivo receptor. Además utiliza ciertos algoritmos para simular rutas "fantasma" por las que quiere enviar al vehículo, pudiendo cambiar las sindicaciones que devuelve tu software de mapas. Gracias a esto el ataque se puede utilizar para enviarte a otro sitio, retrasar a un vehículo de emergencias o, en un caso extremo, provocar un accidente en un coche autónomo.

Este tipo de ataque funciona mejor en áreas urbanas, ya que hay muchas más posibilidades de cambiar rutas y, si son grandes ciudades, es más probable que el conductor no conozca los sitios por los que se mueve. Y mejora aún más si el atacante tiene una idea aproximada de a dónde se dirige el vehículo. Además de pegarse el aparato al fondo del coche, es posible realizar el ataque con éxito usando un dron que vuele por encima del vehículo.

Según los propios investigadores "la amenaza se volverá más realista a medida que los fabricantes de coche añadan capacidades de pilotaje automático a los coches, de modo que las personas estén menos involucradas en la conducción hasta el punto de no estarlo en absoluto cuando los coches autónomos estén en la carretera".

Este tipo de ataques se han venido advirtiendo desde hace años, e incluso hace un lustro se registró un ataque real de este estilo contra un súper-barco de lujo en alta mar. Pero ahora es mucho más accesible a cualquiera, y sobre todo, muy barato. Y cada vez hay más dependencia de este tipo de sistemas.

El estudio, de tan solo 18 páginas, se puede leer online, y contiene bastantes detalles sobre cómo armar el invento y su funcionamiento.

Sería interesante saber si los sistemas equivalentes a GPS, como el GLONASS ruso o el (esperemos que) inminente Galileo europeo, son vulnerables también a este tipo de ataques o no. La mayor parte de los móviles actuales suelen tener soporte también para estos, aunque no los usemos.

Diferentes configuraciones en Visual Studio para generar diferentes ediciones de una aplicación

20/08/2018
Artículo original

Si desarrollamos una aplicación con .NET que se comercializa en distintas ediciones (por ejemplo Standard y Professional), cada una de las cuales ofrece diferentes capacidades/características y diferentes precios, es interesante poder generar físicamente distintos .exe o .dll para cada edición. Por ejemplo, MiSuperApp-Standard.exe y MiSuperApp-PRO.exe.

Una opción muy poco apropiada es mantener dos proyectos diferentes para cada edición, o dos ramas en Git para gestionarlas.

Otra buena opción si no tenemos demasiadas ediciones distintas es la de crear diferentes configuraciones de compilación para poder generar físicamente ensamblados diferentes en cada caso.

Por defecto, cuando creamos un proyecto en Visual Studio solo tenemos dos configuraciones hechas: Debug y Release, que utilizamos respectivamente para desarrollar/depurar la aplicación y generar la versión que vamos a distribuir:

Lo primero que vamos a hacer es disponer de diferentes configuraciones para las diferentes ediciones. Para ello  pulsamos en la opción "Configuration Manager..." del menú de la figura anterior y nos aparecerá una ventana con la gestión de las configuraciones. Creamos una nueva configuración para cada edición de nuestro producto, tal y como muestro en esta animación:

En nuestro ejemplo creamos dos llamados "Standard" (que se ve en la animación anterior) y "Professional". Además eliminamos "Release" ya que no la necesitaremos y las otras dos las hemos copiado de ésta:

Ahora que ya tenemos las configuraciones, lo siguiente que debemos hacer es crear símbolos de compilación condicionada para dichas configuraciones. Esto nos va a permitir posteriormente incluir instrucciones para el compilador de modo que podamos excluir o incluir ciertos fragmentos de código dentro del ensamblado final (.exe o .dll que generemos).

Crear estos símbolos es muy sencillo. Solo es necesario pulsar con el botón derecho sobre el proyecto en el "Explorador de Soluciones" de Visual Studio e irnos a la pestaña "Build". En el desplegable de Configuración elegimos cada uno de las configuraciones nuevas que hemos creado y en el primer cuadro de texto escribimos, en mayúsculas preferiblemente, un nombre sencillo y corto con el que identificar cada una de ellas a la hora de compilar, por ejemplo, en nuestro caso hipotético "STD" y PRO", grabando a continuación. Lo vemos mejor en este mini-vídeo:

Ahora ya tenemos casi todo lo que necesitamos. Vamos a tocar nuestra súper-sencilla aplicación de ejemplo para que muestre por consola algo diferente en función de la configuración. Para ello haremos uso de pragmas condicionales de compilación, que nos permiten tomar decisiones en función de ciertas condiciones. Son líneas especiales que comienzan con una almohadilla. Por ejemplo, podemos escribir:

#if STD
//Código que sea...
#endif

y lo que está entre estos dos "pragmas" solo se compilará en caso de que se cumpla la condición: en este caso solo si estamos en el modo de configuración "Standard", ya que STD es el símbolo que hemos definido para este caso.

También podemos usar un #else:

#if STD
//Código que sea...
#else
//Código en caso de que no se cumpla
#endif

 En nuestro ejemplo podemos verlo en el siguiente vídeo corto:

Con esto ya tenemos mucho andado y conocemos lo básico de trabajar de esta manera.

De todos modos me gustaría comentar algo más que no es tan común:

Cómo generar un .exe o .dll de nombre diferente según la configuración

Puede que nos interese hacer esto y que, en el caso de nuestra hipotética aplicación de ejemplo, se llame de modo diferente según si es la edición estándar o la profesional, por ejemplo: MiApp-STD.exe y MiApp-PRO.exe respectivamente.

Para lograr esto no nos queda más remedio (al menos que yo sepa) que tocar directamente el archivo del proyecto de Visual Studio, es decir, el .csproj que tiene la información sobre el mismo.

Para ello debes cerrar el proyecto en Visual Studio, localizar el archivo .csproj, y abrirlo usando algún editor de texto (el propio Bloc de notas te vale, aunque yo prefiero usar Notepad++ que es mucho mejor y reconoce el archivo correctamente). Una vez abierto debes localizar los nodos <PropertyGroup> de cada una de tus configuraciones y añadirle un nuevo sub-nodo llamado <AssemblyName> que sirve para especificar el nombre exacto que queremos para el ejecutable o DLL final. Indicamos ahí el nombre que queramos, vamos a verlo:

Ahora ya podemos abrir el proyecto de nuevo y compilarlo usando las diferentes configuraciones. Obtendremos un ejecutable diferente según las indicaciones que hemos dado, con el nombre que nos interese. Veámoslo en un último vídeo corto:

Como podemos comprobar obtenemos exactamente lo que buscábamos.

Te dejo el proyecto de ejemplo en un ZIP (5 KB) para que puedas probarlo. Cambia la configuración del proyecto y genera el ejecutable correspondiente para poder probarlo.

¡Espero que te sea útil!

3 formas de inicializar colecciones Java a la hora de declararlas

20/08/2018
Artículo original


Foto por Aaron Burden en Unsplash

En muchos lenguajes, al definir una colección como un array o una lista podemos asignarle una sucesión de valores de forma que los contenga como elementos. En Java, podemos hacer esto con arrays básicos:

int[] miArray = new int[] {3, 1, 4};

Sin embargo, a la hora de inicializar una colección de objetos de una clase, la misma sintaxis no es aplicable. En su lugar, habitualmente debemos recurrir a los métodos que añaden elementos, como add en el caso de las clases con interfaz List:

List<Integer> miLista = new ArrayList<Integer>();
miLista.add(3);
miLista.add(1);
miLista.add(4);

En este post te enseño tres vías para conseguir introducir los elementos en la colección en menos código y con mayor legibilidad.

Métodos de utilidades

Una de las estrategias posibles para hacer más compacto este código es usar el método asList de la clase Arrays de herramientas para manipulación de vectores. Se trata de una solución muy simple que reduciría el código a la siguiente línea:

List<Integer> miLista = Arrays.asList(3, 1, 4);

Un detalle a tener en cuenta es que este método construye una lista de tamaño fijo, por lo que no se podrá ampliar. Para contar con esa posibilidad debemos realizar la conversión a una clase que implemente List:

List<Integer> miLista = new ArrayList<Integer>(Arrays.asList(3, 1, 4));

Inicialización de doble llave

Otra posibilidad es aprovechar un patrón de sintaxis conocida como la inicialización de doble llave. Esta consiste en abrir una llave que crea una subclase anónima, y una segunda llave que comienza un inicializador de instancia. Dentro de este inicializador podemos recurrir a los métodos del objeto que estamos definiendo de la siguiente forma:

List<Integer> miLista = new ArrayList<Integer>() {{ add(3); add(1); add(4); }};

Esta sintaxis es sorprendente y muy versátil, ya que no sólo se reduce a la funcionalidad de añadir elementos sino que se puede utilizar para realizar otras tareas de inicialización, como la definición de componentes de una interfaz de usuario. A pesar de esto, en muchos casos no es recomendada ya que la creación de una subclase anónima puede conllevar cierta sobrecarga.

Métodos factoría de Java 9

Una tercera opción que se ha introducido en Java 9 es el uso de métodos factoría ya incluidos en el lenguaje. Las interfaces List, Set y Map proporcionan un método of que nos permite hacer justo lo que necesitamos: especificar varios elementos a la hora de declarar una colección. El siguiente es un ejemplo de su uso:

List<Integer> miLista = List.of(3, 1, 4);

Esta opción no es totalmente equivalente a las anteriores ya que el método of construye colecciones inmutables. Para permitir modificar y añadir elementos, tendríamos que recurrir al constructor de la colección que deseemos:

List<Integer> miLista = new ArrayList<Integer>(List.of(3, 1, 4));

Espero que estos trucos te sean útiles para manejar pequeñas colecciones más fácilmente en Java.

Fuentes y más información:

SQL Server: cuáles son las diferencias entre @@identity y Scope_identity

14/08/2018
Artículo original


Foto de Kyle Glenn en Unsplash

Cuando insertamos un registro en una tabla de una base de datos, lo más habitual es que se genere automáticamente un nuevo identificador único para el nuevo registro, generalmente un autonumérico. Esto significa que empezamos en el 1 y para cada nuevo registro que añadimos se va aumentando de manera automática a 2, 3, 4... y así sucesivamente. Esto que parece una tontería, tiene muchas implicaciones ya que este identificador es el que vamos a utilizar luego para relacionar el nuevo registro con cualquier otra tabla de la base de datos que necesite utilizarlo. Por ejemplo, una factura y sus líneas de factura están relacionadas a partir del identificador de la cabecera. Cosas por el estilo...

El caso es que la generación de este autonumérico, que parece una tontería, es mucho más compleja de lo que parece ya que se debe tener en cuenta una cosa muy importante: la simultaneidad. Cuando tú haces pruebas en tu equipo de desarrollo, añadir nuevos registros es muy fácil: solo estás tú. Pero en un sistema real en el que pueden estar entrando miles de usuarios a la vez y añadiendo registros en las mismas tablas, el sistema debe asegurar que la secuencia se cumple sin dejar huecos y que no se producen bloqueos importantes.

Una manera ingenua de obtener el autonumérico de un registro que acabas de insertar sería simplemente obtener el máximo de esos números justo a continuación de hacer el INSERT correspondiente. Pero en un sistema en producción no te funcionará con garantías salvo que provoques un bloqueo, ya que mucha gente puede estar introduciendo datos a la vez y entre que insertas el tuyo y lees el máximo puede haberse insertado otro. Es un problema peliagudo.

Cada sistema gestor de base de datos relacionales (SGBDR) que hay en el mercado aborda el problema con su propia filosofía. Ya os hemos explicado aquí cómo obtener el último autonumérico insertado en los más utilizados: Oracle, MySQL y SQL Server.

En el caso de SQL Server explicábamos que la forma de hacerlo era utilizando la instrucción SCOPE_IDENTITY(). Puedes repasarlo en el enlace anterior. Sin embargo SQL Server ofrece al menos 3 formas de obtener el último ID insertado. Aunque puedan parecer iguales, no son equivalentes y conocer sus diferencias es muy importante:

  • @@Identity: devuelve el último ID en la misma conexión.
  • Scope_identity(): la que recomendamos, devuelve el último ID creado en la misma conexión y el mismo contexto (de ahí su nombre). En este caso el contexto se refiere a la consulta o procedimiento almacenado actual.
  • ident_current(nombre): devuelve el último ID de la tabla que le indiquemos (el que haya en ese momento)

En la mayor parte de los casos @@identity y scope_identity() se comportan igual, pero no siempre es así.

Por ejemplo, si tenemos un disparador (trigger) en nuestra tabla y lanzamos una consulta de inserción que genera un nuevo registro y por lo tanto un nuevo ID, si el trigger a su vez genera otro registro en otra tabla (y probablemente otro ID en ésta), en esta situación @@identity nos devolverá el ID del registro que ha insertado el disparador, no el ID de la tabla que hemos actualizado (lo cual no es lo que queremos casi nunca), ya que te devuelve el último que se haya generado en esa conexión, tal y comentábamos en la lista anterior. Sin embargo, en este ejemplo, scope_identity() nos devuelve el identificador que esperábamos (el de la inserción en nuestra tabla) ya que siempre devuelve el del contexto actual, que en este caso es nuestra consulta de inserción.

Este es el motivo de que normalmente se recomiende el uso de scope_identity().

¡Espero que te resulte útil!

Odio las excepciones verificadas

10/08/2018
Artículo original

De verdad que deberían haber eliminado por completo las excepciones verificadas para Java 8. Estorban. No es una coincidencia que casi todos los demás lenguajes de la JVM las eliminaron.

BEGIN RANT

Tengo una clase donde implemento un método abstracto más o menos así:

@Override
protected Runnable crearTarea(Request req) {
  Response resp = new Respuesta(Errores.CANT_CONNECT);
  if (req.getTipo() == Request.UnaCosa) {
    return () -> {
      try {
        unaCosa(req, resp);
      } catch (IOException ex) {
        log.error("Haciendo una cosa con {}", req, ex);
      } finally {
        procesaRespuesta(resp);
      }
    };
  } else if (req.getTipo() == Request.OtraCosa) {
    return () -> {
      try {
        otraCosa(req, resp);
      } catch (IOException ex) {
        log.error("Haciendo otra cosa con {}", req, ex);
      } finally {
        procesaRespuesta(resp);
      }
    };
  } else if (req.getTipo() == Request.OoootraCosa) {
    return () -> {
      try {
        oootraCosa(req, resp);
      } catch (IOException ex) {
        log.error("Haciendo todavía otra cosa con {}", req, ex);
      } finally {

leer más

Cómo postear a Instagram desde el ordenador

09/08/2018
Artículo original

Una de las redes sociales que más me gustan en la actualidad es Instagram. La llevo usando desde hace años porque me gusta la fotografía y modestamente hago mis "pinitos" en este ámbito, pero últimamente la uso mucho más y veo que tiene cada vez una tracción mayor, con más gente, temas interesantes de todo tipo y con una gran ventaja: la inmediatez que te da una imagen o un vídeo corto. Tiene sus pegas, claro está (que es de Facebook, por ejemplo), y no vale para todo ni para expresar ideas muy complejas, pero está bastante bien.

Dado que la mayor parte de las fotografías las tomas hoy en día con el móvil tiene bastante lógica que sea una aplicación nativa para este medio. Pero a veces puede resultar útil poder enviar fotografías que ya tienes en el ordenador. Bien porque las has pasado allí desde una cámara profesional, bien porque las has creado con Photoshop o similar, etc... Aunque Instagram tiene una versión web que puedes usar desde un navegador moderno cualquiera, solo te permite ver los posts, comentar y poco más. No permite enviar fotografías desde el ordenador.

Esto es un fastidio porque te obliga a pasar al móvil tus fotos (usando un disco cloud, Telegram o incluso un cable, como los Neandertales) y hace que todo el proceso sea mucho más ineficiente de lo que debiera.

Por suerte siempre hay alguna forma de saltarse las normas absurdas, y en este caso no es una excepción.

Para conseguirlo lo único que tenemos que hacer es simular que nos conectamos desde un navegador móvil, como si accediésemos a la web de Instagram usando un iPhone o un dispositivo con Android. La versión móvil de la aplicación Web sí que permite enviar fotos.

Tiene algunas limitaciones:

  • No puedes subir vídeos
  • No puedes crear stories (aunque sí verlas)
  • No tienes disponibles todos los filtros
  • No puedes usar las herramientas de edición de imágenes (como Tilt&Shift)

De hecho los dos últimos puntos es muy probable que no te importen en absoluto, ya que en el ordenador puedes hacer procesos a la imagen mucho más potentes que los que ofrece Instagram, y usar programas especializados. Seguramente irán solucionando todas estas cosas en el futuro, pero a día de hoy (agosto de 2018) es lo que hay.

Pero aún así merece mucho la pena usarla desde el ordenador.

A continuación te he dejado un vídeo explicativo en el que te cuento paso a paso qué debes hacer para conseguirlo. Verás que es muy sencillo y funciona en todos los sistemas operativos y en cualquier navegador moderno. Yo he utilizado Chrome en Windows 10, pero no te será complicado hacer lo mismo con otros navegadores como Firefox u Opera, y el vídeo sería exactamente igual si lo hubiese grabado en Mac o Linux.

Allá va:

[youtube:FzhjB6untmQ]

¡Espero que te resulte útil!

¡Ah! y no te olvides de seguirme en Instagram :-)

Funciones reguladoras en JavaScript: cómo limitar el número de veces que se puede llamar a una función cada segundo (throttling y debouncing)

05/08/2018
Artículo original

En muchas ocasiones tenemos la necesidad de ejecutar en una aplicación la misma función JavaScript muchas veces seguidas, incluso sin pretenderlo.

El ejemplo habitual que se suele poner es detectar el redimensionado de una página. Si debes redistribuir y redimensionar mediante código muchos elementos o redibujar un canvas a medida que cambia y que tiene que repintar muchas entidades que el usuario ha definido (en un programa de dibujo, por ejemplo), el proceso que se lleva a cabo en cada cambio de tamaño puede ser largo. Y este evento de redimensionado se llama muchas veces a medida que el usuario modifica el tamaño de la ventana. Pero existen muchas otras situaciones en las que puede ocurrir: encadenar muchas llamadas AJAX en paralelo a un servicio para traer información (p.ej. una búsqueda) a medida que un usuario teclea algo en un cuadro de texto... Cosas por el estilo.

Los motores modernos de JavaScript son capaces de conseguir rendimientos para el código cercanos al del código nativo y, en contra de lo que mucha gente suele pensar, es un lenguaje muy rápido incluso ejecutándolo en un navegador. A pesar de eso en ciertos casos esa llamada indiscriminada a un evento o a una función, si es costosa, puede llegar a bloquear la interfaz de usuario o al menos hacer que ésta no vaya todo lo ágil que debiera. Si se lo permitimos, el navegador hará la llamada todas las veces que sea necesario, acaparando el hilo de ejecución.

En otras ocasiones, aunque no se note ese bloqueo por ser un proceso sencillo y rápido, es una tontería estar llamando 300 veces por segundo a una función si no es necesario. Si con llamarla 5 o 10 veces por segundo es suficiente y los usuarios no van a notar la diferencia, ¿para qué hacer otra cosa?.

Para evitar estos problemas, existen un par de técnicas avanzadas de control denominadas regulación (throttling) y anti-rebote (debouncing, por cierto un término derivado de la electrónica, como muchos otros en programación). Ambas limitan el número de veces que se puede ejecutar una función en un intervalo de tiempo determinado, pero el primero la ejecuta y bloquea la ejecución hasta que pase el intervalo señalado, y la segunda es al revés: no permite que se ejecute hasta que pase como mínimo dicho intervalo (o sea, retrasa la ejecución también) y además mientras se repita no la ejecuta tampoco. En esta página de demo podemos ver la diferencia. La primera es la que más interesante me parece y a la que más casos prácticos le veo, mientras que la segunda la encuentro interesante para casos más restringidos, como por ejemplo no ejecutar un evento keypress hasta que el usuario deje de teclear en un cuadro de texto, y luego ejecutarlo al cabo de x ms.

Nota: quiero agradecer a Pedro J. Molina sus comentarios al respecto de estos matices entre ambos métodos, que había dejado fuera en la versión inicial del artículo y acabo de añadir en el párrafo anterior para mayor claridad a raíz de un comentario suyo en Twitter. ¡Gracias Pedro!

Vamos a ver cómo se suele abordar el problema y luego entraremos en detalle de una solución propia que he creado para la ocasión.

La función clásica de regulación (throttling) y anti-rebote (debouncing)

El método más conocido y replicado hasta la saciedad por Internet de hacer la regulación es el que define la archiconocida biblioteca de utilidades JavaScript Underscore (que te recomiendo conocer porque es muy útil). Esta biblioteca tiene un método llamado debounce() que sirve precisamente para hacer esto. Se usa de la siguiente manera:

var funcionRegulada = _.debounce(funcionNormal, 200);

lo que hace es devolver una versión de la función que queremos llamar que solo se podrá llamar una vez cada 200 milisegundos (es decir 5 veces cada segundo como máximo).

El código que utiliza actualmente lo puedes ver aquí, pero es un poquito más complicado que el original, aunque añade dependencias de otros métodos de underscore.

Es más directo y sencillo el que usaba originalmente hace unos pocos años, que no tenía dependencias en otras funciones propias y era así:

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);
	};
};

No voy a entrar en explicarlo detalladamente porque, como digo, si tienes los fundamentos avanzados de JavaScript claros no es complejo, pero básicamente lo que hace es devolver una función nueva, que sustituye a la original, y que establece un temporizador para ejecutar la verdadera función al cabo del tiempo que se le indique.

Posee un parámetro opcional para forzar la ejecución de la función al principio del intervalo, no al final. Si no lo usamos lo que hace la función es meter un retraso y no permitir la ejecución como mínimo hasta que pase éste, por mucho que sea llamado varias veces antes.

En su versión actual además se pueden cancelar las llamadas reguladas, pero tanto esto como lo anterior no dejan de ser florituras.

En mi opinión complica un poco el uso (yo generalmente lo usaría con true en el tercer parámetro opcional o pierde bastante la gracia).

Mi propia función reguladora (throttling)

Conociendo lo anterior me apetecía intentar mi propia función reguladora que funcionase exactamente cómo yo quería (regulación, no anti-rebote), más fácil de usar (sin tener que pensar en ese tercer parámetro y sus implicaciones) y más obvia sobre lo que está haciendo: bloqueando de verdad la ejecución y limitándola a un determinado número de veces por segundo como máximo, que me parece más natural que indicar el tiempo en milisegundos y decidir si se debe ejecutar antes o después.

Así, he creado la función limitarLLamadas() a la cual se le pasa la función original que queremos limitar y se le indica el número máximo de llamadas que queremos permitir en un segundo.

Ahora explico cómo funciona y te dejo una descarga, pero antes vamos a ver cuál es su efecto.

He creado una página sencilla que tiene mucho texto y que por lo tanto permite hacer scroll arriba y abajo en el contenido (mejor si lo haces estrechando la ventana en caso de que tengas mucha resolución de pantalla). Se detecta y gestiona el evento scroll de la página de modo que cada vez que se mueve el contenido se lleva a cabo una acción como resultado de dicho evento. En este caso simplemente he definido dos funciones: una que cuenta cuántas veces se llama al evento de scroll y la otra que cuenta cuántas veces se llama a una función específica que en este caso no hace nada (solo aumenta un contador) pero que en un caso real sería nuestra función "costosa" que se llama muchas veces. Ambas funciones muestran en una esquinita de la página los contadores, de modo que primero tenemos el número de eventos de scroll que se han lanzado y luego el número de veces que se ha ejecutado la función que nos interesa. Por ejemplo, si pone "100 / 32" significa que el evento de scroll se ha lanzado 100 veces pero que nuestra función (regulada) solo se ha ejecutado 32 veces.

window.addEventListener('scroll', scrollDetectado);
window.addEventListener('scroll', scrollGestionado, 10);

Con esto llamaríamos a las dos funciones cada vez que se haga scroll. La segunda es la que nos interesa limitar. En este caso, al no estar regulada y ser una función normal, se llaman ambas siempre el mismo número de veces. Si lanzamos la página y empezamos a hacer scroll arriba y abajo a toda velocidad con la rueda del ratón veremos esto:

Como podemos apreciar, los dos números del contador siempre son iguales ya que ambas funciones (la que cuenta y la que "gestiona" el evento) se llaman el mismo numero de veces. Y puede llegar a ser muy alto.

Sin embargo si en el código ponemos esto (fíjate en cómo limitamos las llamadas a la función a un máximo de 10 veces por segundo):

window.addEventListener('scroll', scrollDetectado);
window.addEventListener('scroll', limitarLLamadas(scrollGestionado, 10));

...lo que veríamos en este caso es algo similar a lo de este pequeño vídeo:

¿Cómo funciona?

Vamos a echar un vistazo el código de la función que he creado, que como verás no es nada complicado (parece largo pero es por los comentarios explicativos. Sin ellos se queda en nada):

function limitarLLamadas(func, maxXSeg) {
    var bloqueoActivado = false;  //Sirve para indicar que está bloqueada a la función
    
	return function() {
        //Esta función interna simplemente desbloquea las llamadas
		var anularBloqueo = function() {
			bloqueoActivado = false;   //Anulamos el bloqueo
        };

        if (!bloqueoActivado) {
            //Si no hay un bloqueo, llamamos a la función inmediatamente
            func.apply(this, arguments);
            //Bloqueamos
            bloqueoActivado = true;
            //Y desbloqueamos cuando sea necesario para evitar llamadas innecesarias
            setTimeout(anularBloqueo, 1000/maxXSeg);
        }
	};
}

Lo que hace es, en el fondo, parecido a lo que hace underscore (tampoco hay muchas otras formas de plantear la base) en el sentido de que devuelve una nueva función que "envuelve" a la original para dotarla de limitaciones a la hora de llamarla. Pero en este caso utilizo el temporizador solamente para desbloquear después de un tiempo las posibles llamadas a la función original. De este modo se regula muy bien esta limitación y me parece una forma mucho más natural de hacerlo. Aunque puedo estar equivocado, por supuesto.

Se establece una variable booleana para determinar si las llamadas están bloqueadas o no (la primera y la siguiente tras el ultimo bloqueo no lo están nunca, claro), y defino una función anularBloqueo() que, como su propio nombre indica, lo único que hace es establecer la variable anterior de nuevo a false.

La primera vez que se llama a la función se llama al método original y acto seguido se establece un bloqueo (bloqueoActivado = true;). Mientras dure éste no se permiten más llamadas a la función original (debido al if (!bloqueoActivado)). Además se establece un temporizador que llama a la función anularBloqueo() que hemos visto hace un instante para liberar dicho bloqueo al cabo del tiempo apropiado, que se obtiene de dividir el número de milisegundos en un segundo (1000, claro) entre el número máximo de veces que queremos permitir que se llame a la función.

Por cierto, si quisiésemos limitar el máximo de llamadas aún más, por ejemplo a 1 cada dos segundos, podríamos pasarle un decimal. Por ejemplo: limitarLLamadas(scrollGestionado, 0.5) limitaría las llamadas a 1 cada dos segundos.

Parece (y es) más sencillo y creo que funciona muy bien.

Te dejo una descarga (ZIP, 7.24KB) con el ejemplo y un pequeño archivo debounce.js con el código de la función.

¡Espero que te haya resultado interesante y que te sea útil!

Funciones reguladoras en JavaScript: cómo limitar el número de veces que se puede llamar a una función cada segundo (debouncing)

04/08/2018
Artículo original

En muchas ocasiones tenemos la necesidad de ejecutar en una aplicación la misma función JavaScript muchas veces seguidas, incluso sin pretenderlo.

El ejemplo habitual que se suele poner es detectar el redimensionado de una página. Si debes redistribuir y redimensionar mediante código muchos elementos o redibujar un canvas a medida que cambia y que tiene que repintar muchas entidades que el usuario ha definido (en un programa de dibujo, por ejemplo), el proceso que se lleva a cabo en cada cambio de tamaño puede ser largo. Y este evento de redimensionado se llama muchas veces a medida que el usuario modifica el tamaño de la ventana. Pero existen muchas otras situaciones en las que puede ocurrir: encadenar muchas llamadas AJAX en paralelo a un servicio para traer información (p.ej. una búsqueda) a medida que un usuario teclea algo en un cuadro de texto... Cosas por el estilo.

Los motores modernos de JavaScript son capaces de conseguir rendimientos para el código cercanos al del código nativo y, en contra de lo que mucha gente suele pensar, es un lenguaje muy rápido incluso ejecutándolo en un navegador. A pesar de eso en ciertos casos esa llamada indiscriminada a un evento o a una función, si es costosa, puede llegar a bloquear la interfaz de usuario o al menos hacer que ésta no vaya todo lo ágil que debiera. Si se lo permitimos, el navegador hará la llamada todas las veces que sea necesario, acaparando el hilo de ejecución.

En otras ocasiones, aunque no se note ese bloqueo por ser un proceso sencillo y rápido, es una tontería estar llamando 300 veces por segundo a una función si no es necesario. Si con llamarla 5 o 10 veces por segundo es suficiente y los usuarios no van a notar la diferencia, ¿para qué hacer otra cosa?.

Para evitar estos problemas, existe una técnica avanzada de control denominada regulación, muchas veces conocida por su término en inglés: debouncing (por cierto un término derivado de la electrónica, como muchos otros en programación).

Vamos a ver cómo se suele abordar el problema y luego entraremos en detalle de una solución propia que he creado para la ocasión.

La función clásica de regulación o debouncing

El método más conocido y replicado hasta la saciedad por Internet de hacer la regulación es el que define la archiconocida biblioteca de utilidades JavaScript Underscore (que te recomiendo conocer porque es muy útil). Esta biblioteca tiene un método llamado debounce() que sirve precisamente para hacer esto. Se usa de la siguiente manera:

var funcionRegulada = _.debounce(funcionNormal, 200);

lo que hace es devolver una versión de la función que queremos llamar que solo se podrá llamar una vez cada 200 milisegundos (es decir 5 veces cada segundo como máximo).

El código que utiliza actualmente lo puedes ver aquí, pero es un poquito más complicado que el original, aunque añade dependencias de otros métodos de underscore.

Es más directo y sencillo el que usaba originalmente hace unos pocos años, que no tenía dependencias en otras funciones propias y era así:

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);
	};
};

No voy a entrar en explicarlo detalladamente porque, como digo, si tienes los fundamentos avanzados de JavaScript claros no es complejo, pero básicamente lo que hace es devolver una función nueva, que sustituye a la original, y que establece un temporizador para ejecutar la verdadera función al cabo del tiempo que se le indique.

Posee un parámetro opcional para forzar la ejecución de la función al principio del intervalo, no al final.

En su versión actual además se pueden cancelar las llamadas reguladas, pero tanto esto como lo anterior no dejan de ser florituras.

En mi opinión complica un poco el uso (yo generalmente lo usaría con true en el tercer parámetro opcional o pierde bastante la gracia).

Mi propia función reguladora

Conociendo lo anterior me apetecía intentar mi propia función reguladora que funcionase exactamente cómo yo quería, más fácil de usar (sin tener qe pensar en ese tercer parámetro y sus implicaciones) y más obvia sobre lo que está haciendo: bloqueando de verdad la ejecución y limitándola a un determinado número de veces por segundo como máximo, que me parece más natural que indicar el tiempo en milisegundos.

Así, he creado la función limitarLLamadas() a la cual se le pasa la función original que queremos limitar y se le indica el número máximo de llamadas que queremos permitir en un segundo.

Ahora explico cómo funciona y te dejo una descarga, pero antes vamos a ver cuál es su efecto.

He creado una página sencilla que tiene mucho texto y que por lo tanto permite hacer scroll arriba y abajo en el contenido (mejor si lo haces estrechando la ventana en caso de que tengas mucha resolución de pantalla). Se detecta y gestiona el evento scroll de la página de modo que cada vez que se mueve el contenido se lleva a cabo una acción como resultado de dicho evento. En este caso simplemente he definido dos funciones: una que cuenta cuántas veces se llama al evento de scroll y la otra que cuenta cuántas veces se llama a una función específica que en este caso no hace nada (solo aumenta un contador) pero que en un caso real sería nuestra función "costosa" que se llama muchas veces. Ambas funciones muestran en una esquinita de la página los contadores, de modo que primero tenemos el número de eventos de scroll que se han lanzado y luego el número de veces que se ha ejecutado la función que nos interesa. Por ejemplo, si pone "100 / 32" significa que el evento de scroll se ha lanzado 100 veces pero que nuestra función (regulada) solo se ha ejecutado 32 veces.

window.addEventListener('scroll', scrollDetectado);
window.addEventListener('scroll', scrollGestionado, 10);

Con esto llamaríamos a las dos funciones cada vez que se haga scroll. La segunda es la que nos interesa limitar. En este caso, al no estar regulada y ser una función normal, se llaman ambas siempre el mismo número de veces. Si lanzamos la página y empezamos a hacer scroll arriba y abajo a toda velocidad con la rueda del ratón veremos esto:

Como podemos apreciar, los dos números del contador siempre son iguales ya que ambas funciones (la que cuenta y la que "gestiona" el evento) se llaman el mismo numero de veces. Y puede llegar a ser muy alto.

Sin embargo si en el código ponemos esto (fíjate en cómo limitamos las llamadas a la función a un máximo de 10 veces por segundo):

window.addEventListener('scroll', scrollDetectado);
window.addEventListener('scroll', limitarLLamadas(scrollGestionado, 10));

...lo que veríamos en este caso es algo similar a lo de este pequeño vídeo:

¿Cómo funciona?

Vamos a echar un vistazo el código de la función que he creado, que como verás no es nada complicado (parece largo pero es por los comentarios explicativos. Sin ellos se queda en nada):

function limitarLLamadas(func, maxXSeg) {
    var bloqueoActivado = false;  //Sirve para indicar que está bloqueada a la función
    
	return function() {
        //Esta función interna simplemente desbloquea las llamadas
		var anularBloqueo = function() {
			bloqueoActivado = false;   //Anulamos el bloqueo
        };

        if (!bloqueoActivado) {
            //Si no hay un bloqueo, llamamos a la función inmediatamente
            func.apply(this, arguments);
            //Bloqueamos
            bloqueoActivado = true;
            //Y desbloqueamos cuando sea necesario para evitar llamadas innecesarias
            setTimeout(anularBloqueo, 1000/maxXSeg);
        }
	};
}

Lo que hace es, en el fondo, parecido a lo que hace underscore (tampoco hay muchas otras formas de plantear la base) en el sentido de que devuelve una nueva función que "envuelve" a la original para dotarla de limitaciones a la hora de llamarla. Pero en este caso utilizo el temporizador solamente para desbloquear después de un tiempo las posibles llamadas a la función original. De este modo se regula muy bien esta limitación y me parece una forma mucho más natural de hacerlo. Aunque puedo estar equivocado, por supuesto.

Se establece una variable booleana para determinar si las llamadas están bloqueadas o no (la primera y la siguiente tras el ultimo bloqueo no lo están nunca, claro), y defino una función anularBloqueo() que, como su propio nombre indica, lo único que hace es establecer la variable anterior de nuevo a false.

La primera vez que se llama a la función se llama al método original y acto seguido se establece un bloqueo (bloqueoActivado = true;). Mientras dure éste no se permiten más llamadas a la función original (debido al if (!bloqueoActivado)). Además se establece un temporizador que llama a la función anularBloqueo() que hemos visto hace un instante para liberar dicho bloqueo al cabo del tiempo apropiado, que se obtiene de dividir el número de milisegundos en un segundo (1000, claro) entre el número máximo de veces que queremos permitir que se llame a la función.

Por cierto, si quisiésemos limitar el máximo de llamadas aún más, por ejemplo a 1 cada dos segundos, podríamos pasarle un decimal. Por ejemplo: limitarLLamadas(scrollGestionado, 0.5) limitaría las llamadas a 1 cada dos segundos.

Parece (y es) más sencillo y creo que funciona muy bien.

Te dejo una descarga (ZIP, 7.24KB) con el ejemplo y un pequeño archivo debounce.js con el código de la función.

¡Espero que te haya resultado interesante y que te sea útil!

Página Anterior Página Siguiente