EDITAR: según el comentario de Yogi (consulte "setTimeout" y "throttling" en https://developer.mozilla.org/en-US/docs/Web/API/setTimeout ), he intentado agregar un AudioContext para evitar la desaceleración.
document.addEventListener('click', ev => { let audCtxt = new AudioContext({}); });(AudioContext necesita la interacción del usuario, por lo tanto, el detector de eventos).
Pero, sin suerte.
Otras ideas que apunto aquí para seguir son
while , verificando Date.now() para múltiplos de 10 ms, pero creo que eso bloquearía la páginarequestAnimationFrame ?Publicación original:
Tengo un setTimeout que se activa cada 0,01 segundos que actúa como un reloj maestro para mi aplicación web.
La aplicación reproduce sonidos sincronizados que responden a la interacción del usuario, de ahí la necesidad de un reloj maestro. Simplificado:
let counter = 0; setTimeout(() => { counter++; console.log(counter); }, 10);Cuando está en un dispositivo móvil, setTimeout se ralentiza (alrededor de 2 a 4 veces) cuando la pantalla está bloqueada/apagada. (Probado en Android, no en iOS).
Esto se puede verificar registrando, como el anterior, o generando un sonido cuando el contador es múltiplo de 100.
setTimeout no es confiable ya que otras cosas, como las promesas, tienen una mayor prioridad de ejecución. Una posible solución es crear un temporizador personalizado usando promesas. Aquí hay un ejemplo:
var customDelay = new Promise(function (resolve) { var delay = 10; // milliseconds var before = Date.now(); while (Date.now() < before + delay) { }; resolve(); }); customDelay.then(function () { //Timer triggered });Actualización 1:
Dado que desea una frecuencia de actualización de 10 ms, ejecutar el código anterior en el subproceso principal termina bloqueando la interfaz de usuario debido al bucle while. Con eso en mente, descargar ese bucle while en un trabajador web resolvería esto. Aquí hay algo de código:
<html> <head> <title></title> </head> <body> <script id="FastTimer" type="javascript/worker"> onmessage = function (event) { var delay = 10; // milliseconds var before = Date.now(); while (Date.now() < before + delay) { }; postMessage({data: []}); }; </script> <script> var worker; window.onload = function() { var blob = new Blob([document.querySelector("#FastTimer").textContent]); blobURL = window.URL.createObjectURL(blob); worker = new Worker(blobURL); worker.addEventListener("message", receivedWorkerMessage); worker.onerror = workerError; //Start the worker. worker.postMessage({}); } var counter = 0; function receivedWorkerMessage(event) { worker.postMessage({}); timerTiggered(); } function timerTiggered() { counter++; console.log(counter); } function workerError(error) { alert(error.message); } function stopWorker() { worker.terminate(); worker = null; } </script> </body> </html>El problema principal con lo anterior es que sospecho que habría algún tipo de costo de tiempo entre el trabajador (tal vez un par de ms, difícil de decir).
Como se mencionó, normalmente requestAnimationFrame se usa para animaciones en aplicaciones web. Sin embargo, es probable que esto no se active cuando la pantalla está bloqueada. Pero si quieres probar, aquí tienes una muestra:
<html> <head> <title></title> </head> <body> <script> var counter = 0; var minTimeSpan = 10; var lastTime = performance.now(); function animate() { let t = performance.now(); if (t - lastTime >= minTimeSpan) { timerTiggered(); } requestAnimationFrame(animate); } function timerTiggered() { counter++; console.log(counter); } animate(); </script> </body> </html>Por lo general, esto se puede resolver utilizando un Web Worker para ejecutar el temporizador. Creé una biblioteca que se parece a setTimeout() pero usa un Web Worker internamente.
https://github.com/chrisguttandin/worker-timers
Pero hubo un error hace un tiempo en algún navegador (olvidé cuál era) que hizo que esto ya no funcionara. Por lo tanto, construí la misma abstracción sobre un AudioContext en ejecución.
https://github.com/chrisguttandin/audio-context-timers
Pero como ya dijiste, esto solo funciona si la página ya tiene permitido ejecutar un AudioContext iniciando uno en respuesta a un controlador de clic.