¿Cuál es la razón por la que JavaScript setTimeout es tan inexacto?

Resuelto Tomás asked hace 10 años • 5 respuestas

Tengo este código aquí:

var date = new Date();
setTimeout(function(e) {
    var currentDate = new Date();
    if(currentDate - date >= 1000) {
         console.log(currentDate, date);
         console.log(currentDate-date);
    }
    else {
       console.log("It was less than a second!");
       console.log(currentDate-date);
    }
}, 1000);

En mi computadora siempre se ejecuta correctamente, con 1000 en la salida de la consola. Curiosamente, en otra computadora, el mismo código, la devolución de llamada del tiempo de espera comienza en menos de un segundo y la diferencia currentDate - dateestá entre 980 y 998.

Conozco la existencia de bibliotecas que solucionan esta inexactitud (por ejemplo, Tock ).

Básicamente, mi pregunta es: ¿ Cuáles son las razones por las que setTimeoutno se dispara en el retraso indicado? ¿Podría ser que la computadora es demasiado lenta y el navegador intenta automáticamente adaptarse a la lentitud y activa el evento antes?

PD: Aquí hay una captura de pantalla del código y los resultados ejecutados en la consola JavaScript de Chrome:

Ingrese la descripción de la imagen aquí

Tomás avatar Jan 14 '14 00:01 Tomás
Aceptado

No se supone que sea particularmente preciso. Hay una serie de factores que limitan la rapidez con la que el navegador puede ejecutar el código; citando de MDN :

Además de "sujetar", el tiempo de espera también puede activarse más tarde cuando la página (o el sistema operativo/navegador) esté ocupado con otras tareas.

En otras palabras, la forma en que setTimeoutgeneralmente se implementa es solo para ejecutarse después de un retraso determinado y una vez que el hilo del navegador esté libre para ejecutarlo.

Sin embargo, diferentes navegadores pueden implementarlo de diferentes maneras. Aquí hay algunas pruebas que hice:

var date = new Date();
setTimeout(function(e) {
    var currentDate = new Date();
    console.log(currentDate-date);
}, 1000);

// Browser Test1 Test2 Test3 Test4
// Chrome    998  1014   998   998
// Firefox  1000  1001  1047  1000
// IE 11    1006  1013  1007  1005

Tal vez el <1000 veces de Chrome podría atribuirse a una inexactitud en el Datetipo, o tal vez podría ser que Chrome use una estrategia diferente para decidir cuándo ejecutar el código; tal vez esté tratando de encajarlo en el intervalo de tiempo más cercano, incluso si el tiempo de espera aún no se ha completado.

En resumen, no debería usarlo setTimeoutsi espera una sincronización confiable y consistente en una escala de milisegundos.

p.s.w.g avatar Jan 13 '2014 17:01 p.s.w.g

En general, los programas informáticos son muy poco fiables cuando intentan ejecutar cosas con una precisión superior a 50 ms. La razón de esto es que incluso en un procesador hyperthreaded octacore el sistema operativo generalmente hace malabarismos con varios cientos de procesos y subprocesos, a veces miles o más. El sistema operativo hace que toda esa multitarea funcione programándolas todas para obtener una porción de tiempo de CPU, una tras otra, lo que significa que tienen "unos pocos milisegundos de tiempo como máximo para hacer lo suyo".

Implícitamente, esto significa que si establece un tiempo de espera de 1000 ms, las posibilidades no son pequeñas de que el proceso actual del navegador ni siquiera se esté ejecutando en ese momento, por lo que es perfectamente normal que el navegador no se dé cuenta hasta 1005, 1010 o incluso 1050 milisegundos que debería estar ejecutando la devolución de llamada dada.

Generalmente esto no es un problema, sucede y rara vez es de suma importancia. Si es así, todos los sistemas operativos proporcionan temporizadores a nivel de kernel que son mucho más precisos que 1 ms y permiten al desarrollador ejecutar código precisamente en el momento correcto. Sin embargo, JavaScript, como entorno fuertemente aislado, no tiene acceso a objetos del kernel como ese, y los navegadores se abstienen de usarlos ya que, en teoría, podría permitir que alguien ataque la estabilidad del sistema operativo desde el interior de una página web, construyendo cuidadosamente código que priva a otros. hilos inundándolos con muchos temporizadores peligrosos.

En cuanto a por qué la prueba arroja 980, no estoy seguro; eso dependerá exactamente de qué navegador esté utilizando y qué motor de JavaScript. Sin embargo, puedo entender completamente si el navegador corrige manualmente un poco hacia abajo la carga y/o la velocidad del sistema, asegurándose de que "en promedio, el retraso sigue siendo aproximadamente el tiempo correcto": tendría mucho sentido desde el principio de sandboxing simplemente aproximar la cantidad de tiempo requerido sin sobrecargar potencialmente el resto del sistema.

Niels Keurentjes avatar Jan 13 '2014 17:01 Niels Keurentjes

Alguien por favor corríjame si estoy malinterpretando esta información:

Según una publicación de John Resig sobre la inexactitud de las pruebas de rendimiento en todas las plataformas (el énfasis es mío)

Dado que los tiempos del sistema se redondean constantemente hacia abajo hasta el último tiempo consultado ( cada uno con una diferencia de aproximadamente 15 ms ), la calidad de los resultados de rendimiento se ve seriamente comprometida.

Por lo tanto, hay un error de hasta 15 ms en cada extremo en comparación con la hora del sistema.

Evan Davis avatar Jan 13 '2014 17:01 Evan Davis