Seleccionar página

Limitaciones del uso de Date.now()

Cuando uno empieza a realizar los primeros análisis de rendimiento de su código en Javascript es muy común utilizar un par de variables, una antes y otras después del código que queremos comprobar, donde almacenamos la fecha y hora actual obtenida con Date.now() y luego comparamos ambas. No es una técnica incorrecta, pero en muchas ocasiones nuestro código se va a ejecutar con mayor velocidad del que somos capaces de capturar con Date.now(), es decir, obtenemos el mismo valor en las dos variables y por lo tanto el tiempo de ejecución que nos aparece es 0.

var start = new Date ();
var a     = 1000;
var end   = new Date ();
console.log (end - start);

Lo que sucede es que nuestro código a tardado menos de un milisegundo en ejecutarse y, por lo tanto, las dos variables tienen la misma fecha y hora. Date.now() devuelve el número de milisegundos pasados desde el 1 de enero de 1970 a las 0:00 hora UTC. Este método no dispone de mayor precisión que el milisegundo, yaunque nos parezca un periodo de tiempo muy pequeño, los motores modernos de Javascript pueden hacer muchas cosas en 1 milisegundo.

También tenemos que saber que la mayoría de las veces podemos confiar en la comparación de valores de vueltos por new Date(), aunque puedan tener una precisión limitada, pero en algunas ocasiones podemos obtener valores incorrectos por la actualización de la hora del sistema. Hay que tener en cuenta que el reloj del ordenador puede ser actualizado por el usuario o por algún servicio de sincronización de la hora del sistema como NTP y si comparamos dos valores tomados antes y después de esta actualización del reloj podemos, incluso, obtener valores negativos.

Tiempo de alta precisión, relativo y monótono

En 2012 -hace ya bastante tiempo- apareció en los navegadores el API performance.now() para obtener diferencias de tiempos con una precisión elevada (High Resolution Time). Este API está implementada en IE10, Firefox 15, Chrome 24, Safari 8 y Node 8.5 (en este último con require('perf_hooks').performance, que está publicado en forma experimental), por lo que podemos utilizarla sin temor a que no esté disponible en nuestro sistema.

var start = performance.now();
var a     = 1000;
var end   = performance.now();
console.log (end - start);

Las llamadas a performance.now() devuelven un valor de milisegundos, con decimales, con una precisión de milésima de milisegundo (millonesima parte de un segundo, o nanosegundo), por lo que su precisión es muy alta. Dan el tiempo desde el inicio de nuestro programa, no son una fecha y hora como tal, y por lo tanto sólo son útiles si los utilizados como marcas de tiempo, por ejemplo para analizar el rendimiento o hacer algún tipo de marca temporal de alta precisión.

La otra ventaja es que los valores de tiempo devueltos al llamar al método performance.now() son siempre monótonamente crecientes y no están sujetos a los ajustes, sesgo o desviación del reloj del sistema. La diferencia entre cualquiera de los dos valores obtenidos de performance.now() nunca puede ser negativa y crece de forma estable (monótona).

En Node, además de poder utilizar la función por medio del paquete perf_hooks, que en estos momentos está publicado en modo experimental, tenemos a nuestra disposición dos métodos muy sencillos para obtener un tiempo de alta precisión: process.hrtime() que devuelve una matriz con dos valores, uno con el valor en milisegundos y otro los nanosegundos y también el más moderno process.hrtime.bigint() que devuelve directamente un valor de tipo bigint con los nanosegundos.

Si queremos disponer de una función que funcione directamente tanto en navegador como en servidor, podemos construirla con mucha facilidad de esta manera:

/**
 * get high precision relative time
 * @returns {number}
 */
const hrtime =
  (typeof window !== 'undefined' &&
  typeof window.performance !== 'undefined' &&
  window.performance.now) ?
    window.performance.now.bind (window.performance) :
    (typeof require !== 'undefined') ?
      require ('perf_hooks').performance.now :
      function () {
        var h = process.hrtime ();
        return (h[ 0 ] + (h[ 1 ] / 1e9)) * 1000;
      };

var start = hrtime ();
for (var n = 0; n < 10000; n++) {
  var x = Math.log(n);
}
var end   = hrtime ();
console.log (end - start);

Fecha y hora de alta precisión

Aunque no suele ser necesario, si queremos obtener una fecha y hora de alta precision podemos partir de performance.now() y apoyarnos en performance.timeOrigin que tiene el valor inicial a partir del cual está calcuándose el valor devuelto por performance.now().

var start = performance.now() + performance.timeOrigin;
var a     = 1000;
var end   = performance.now() + performance.timeOrigin;
console.log ('start:', start, 'end:', end, 'Date.now()');

El problema es que performance.timeOrigin es relativamente reciente y es posible que no lo encontremos en todos los navegadores y tendríamos, en esos casos, que utilizar otras propiedades ahora deprecadas como performance.timing.navigationStart que, con un poco menos de precisión, son equivalentes.

Seguridad y tiempo de alta precisión

A fin de evitar algunas confusiones, queremos aclarar que el acceso a información precisa de tiempo es una necesidad común para muchas aplicaciones y no estamos haciendo nada incorrecto al usar estas API. Por ejemplo, son imprescindibles para la coordinación de elementos en animaciones o para realizar un análisis del rendimiento de las partes críticas del código.

El problema es que el acceso a esta información de tiempo alta precisión también puede ser utilizada con fines maliciosos por un atacante para obtener o inferir datos, de ahí que se haya producido un cierto debate sobre estas API y su nivel de precisión. Algunos de estos problemas de privacidad y seguridad consisten en que la alta precisión de estas API podrían permitir a un sitio web malintencionado diferenciar entre subconjuntos de usuarios y, en algunos casos extremos, identificar a un usuario en particular.

Algunos navegadores (especialmente Firefox) han incorporado mecanismos para reducir la precisión de estas API y evitar estas actividades de rastreo, pero no se han generalizado. En cualquier caso, a pesar de que puedan ser utilizadas para realizar algún tipo de rastreo, estas son una APIs perfectamente válidas y utilizables sin ningún tipo de prevención.

Novedades

HTTP2 para programadores. Enviar mensajes del servidor al cliente con Server Sent Event (sin WebSockets)

HTTP2 para programadores. Enviar mensajes del servidor al cliente con Server Sent Event (sin WebSockets)

En esta charla, organizada por MadridJS, Pablo Almunia nos muestra cómo la mayoría de nosotros cuando oímos hablar por primera vez de HTTP2 nos ilusionamos con las posibilidades que presumiblemente se abrían para el desarrollo de soluciones web avanzadas y cómo muchos nos sentimos defraudados con lo que realmente se podía implementar.

En esta charla podemos ver cómo funciona el HTTP2, que debemos tener en cuenta en el servidor para hace uso de este protocolo y, sobre todo, cómo podemos enviar información desde el servidor al cliente de forma efectiva y fácil. Veremos con detenimiento cómo por medio de los Server-Sent Events (SSE) podemos recibir en el cliente datos enviados desde el servidor sin utilizar websocket, simplificando enormemente la construcción de aplicaciones con comunicación bidireccional.

Observables en Javascript con Proxies

Observables en Javascript con Proxies

En esta charla, organizada por MadridJS, Pablo Almunia nos habla de la observación reactiva de objetos en Javascript por medio de Proxy. Se describe paso a paso cómo funcionan los Proxies y en que casos pueden ser nuestro mejor aliado. Veremos que no hay que tenerles miedo, son bastante sencillos de utilizar, y nos ofrecen una gran abanico de posibilidades.

Aplicaciones JAMStack, SEO friendly y escalables con NextJS

Aplicaciones JAMStack, SEO friendly y escalables con NextJS

En esta charla de Madrid JS, Rafael Ventura nos describe las funcionalidades clave de NextJS, nos muestra en vivo cómo desarrollar una completa aplicación JAMStack con Server Side Rendering (SSR) y Static Site Generation (SSG) y termina mostrando como publicar esta aplicación en Vercel.

Stencil JS: mejora el Time To Market de tu producto, por Rubén Aguilera

Stencil JS: mejora el Time To Market de tu producto, por Rubén Aguilera

En esta charla Rubén Aguilera nos cuenta los problemas que tienen muchas empresas a la hora de sacar productos accesibles, vistosos y usables en el Time To Market que requiere Negocio y cómo podemos minimizar este tiempo gracias al DevUI con StencilJS para adecuar una aplicación de Angular a las exigencias del mercado en tiempo record.