practices - print variable javascript
Sincronización de tiempo JS entre múltiples dispositivos (5)
¿Qué tal un enfoque diferente: a quién le importa el tiempo? (No sincronizará de forma confiable el reloj del sistema con JavaScript).
En su lugar, use un servidor de nodo con socket.io para sincronizar cuando sus clientes avancen la presentación de diapositivas. En lugar de que los clientes decidan cuándo avanzar, el servidor les dice que lo hagan.
Este enfoque viene con la ventaja añadida de poder virar manualmente la presentación de diapositivas mientras se está ejecutando. En el ejemplo que sigue, he agregado un botón Siguiente que hace que todos los clientes conectados avancen inmediatamente a la siguiente diapositiva.
app.js
var express = require(''express'')
, app = express.createServer()
, io = require(''socket.io'').listen(app)
, doT = require(''dot'')
, slide = 0
, slides = [
''http://placekitten.com/700/400?image=13'',
''http://placekitten.com/700/400?image=14'',
''http://placekitten.com/700/400?image=15'',
''http://placekitten.com/700/400?image=16'',
''http://placekitten.com/700/400?image=1'',
''http://placekitten.com/700/400?image=2'',
''http://placekitten.com/700/400?image=3'',
''http://placekitten.com/700/400?image=4'',
''http://placekitten.com/700/400?image=5'',
''http://placekitten.com/700/400?image=6'',
''http://placekitten.com/700/400?image=7'',
''http://placekitten.com/700/400?image=8'',
''http://placekitten.com/700/400?image=9'',
''http://placekitten.com/700/400?image=10'',
''http://placekitten.com/700/400?image=11'',
''http://placekitten.com/700/400?image=12'',
];
app.listen(70); // listen on port 70
app.register(''.html'', doT); // use doT to render templates
app.set(''view options'', {layout:false}); // keep it simple
doT.templateSettings.strip=false; // don''t strip line endings from template file
app.get(''/'', function(req, res) {
res.render(''index.html'', { slide: slide, slides: slides });
});
app.post(''/next'', function(req, res) {
next();
res.send(204); // No Content
});
setInterval(next, 4000); // advance slides every 4 seconds
function next() {
if (++slide >= slides.length) slide = 0;
io.sockets.emit(''slide'', slide);
}
views / index.html
Este archivo se procesa como una plantilla doT .
<!DOCTYPE html>
<html>
<head>
<title>Synchronized Slideshow</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
var curslide = {{=it.slide}}; // the slide the server is currently on.
$(function() {
$(''#slide'' + curslide).css(''left'',0);
$(''#next'').click(function() {
$.post(''/next'');
});
});
var socket = io.connect(''http://localhost:70'');
socket.on(''slide'', function(slide) {
$(''#slide'' + curslide).animate({left:-700}, 400);
$(''#slide'' + slide).css(''left'',700).show().animate({left:0}, 400);
curslide = slide;
});
</script>
<style>
#slideshow, .slide { width:700px; height:400px; overflow:hidden; position:relative; }
.slide { position:absolute; top:0px; left:700px; }
</style>
</head>
<body>
<div id="slideshow">
{{~it.slides :url:i}}
<div id="slide{{=i}}" class="slide"><img src="{{=url}}"></div>
{{~}}
</div>
<button id="next">Next ></button>
</body>
</html>
Copie estos dos archivos en una carpeta, luego ejecútelos
$ npm install express socket.io dot
$ node app.js
y navegue a http://localhost:70
en varias ventanas diferentes, luego vea la magia.
Estoy usando la maravillosa biblioteca reveal.js para crear una presentación de diapositivas HTML. Mi único problema es que lo necesito para sincronizar en varios dispositivos.
En este momento estoy haciendo una solicitud de AJAX al tiempo del servidor y tengo un reloj interno para la página.
function syncTime() {
// Set up our time object, synced by the HTTP DATE header
// Fetch the page over JS to get just the headers
console.log("syncing time")
var r = new XMLHttpRequest();
r.open(''HEAD'', document.location, false);
r.send(null);
var timestring = r.getResponseHeader("DATE");
systemtime = new Date(timestring); // Set the time to the date sent from the server
}
Si bien esto me da 1 o más segundos de precisión, necesito hacerlo mejor. La diferencia es realmente notable cuando la presentación de diapositivas avanza automáticamente.
El código se ejecutará en la misma plataforma, por lo que no hay que preocuparse por la compatibilidad entre navegadores.
Esto es lo que he logrado juntar
¿Algunas ideas?
Estoy usando extensamente el patrón COMET aquí para mi aplicación web en tiempo real.
Para usar eso en su caso, necesitaría que los clientes abran una solicitud de AJAX al servidor y esperen una respuesta. Tan pronto como se presente, el cliente debe cambiar las diapositivas.
En el servidor, debe retener todas las respuestas hasta que sea el momento de cambiar las diapositivas. (Usted podría estar más avanzado y retrasar después en el cliente por el mismo tiempo cada uno, pero eso probablemente no sea necesario). No puedo mostrar el código de muestra aquí porque no sé qué hay disponible para ti.
Entonces estás creando una orquesta en la que el servidor interpreta al director y todos los clientes lo escuchan.
El tiempo se determina por la capacidad del servidor para responder a las solicitudes en (casi) el mismo tiempo más la latencia de la red.
Por lo general, los clientes deben estar en la misma parte de la red, por lo que la latencia puede ser muy similar, y el valor absoluto no duele aquí, solo la variación.
Y también podría haber un truco adicional para ayudar: no cambie las diapositivas con un reemplazo duro, mezcle. Esto borrará el cambio para que el ojo no pueda captar las pequeñas diferencias de tiempo que siempre tendrás.
(Si no puede hacer que el servidor sea el conductor, es probable que MikeWyatt tenga que usar la solución, probablemente con unas pocas solicitudes y promediando el resultado, dependiendo de la configuración de la red. En una LAN, una solicitud podría ser suficiente; en Internet completo, un poco más de promedio no le hará daño ...)
Me alegra que hayas encontrado una respuesta satisfactoria a tu pregunta. Tenía una necesidad similar de sincronizar el navegador con el reloj del servidor y estaba decidido a lograrlo con una precisión superior a 1 segundo. He escrito el código para hacer esto y estoy publicando esta respuesta aquí en caso de que alguien más también necesite la solución.
El código se llama ServerDate y se puede descargar libremente. Aquí hay una parte del README. Tenga en cuenta que logré una precisión de 108 ms en mi ejemplo:
Puede usar ServerDate
ya que usaría la función Date
o una de sus instancias, por ejemplo:
> ServerDate()
"Mon Aug 13 2012 20:26:34 GMT-0300 (ART)"
> ServerDate.now()
1344900478753
> ServerDate.getMilliseconds()
22
También hay un nuevo método para obtener la precisión de la estimación de ServerDate del reloj del servidor (en milisegundos):
> ServerDate.toLocaleString() + " ± " + ServerDate.getPrecision() + " ms"
"Tue Aug 14 01:01:49 2012 ± 108 ms"
Puede ver la diferencia entre el reloj del servidor y el reloj de los navegadores, en milisegundos:
> ServerDate - new Date()
39
Mida el tiempo transcurrido entre el envío de la solicitud y el regreso de la respuesta. Luego, divide ese valor por 2. Eso te da un valor aproximado de latencia unidireccional. Si agrega eso al valor de tiempo del servidor, estará más cerca de la hora verdadera del servidor.
Algo como esto debería funcionar:
function syncTime() {
// Set up our time object, synced by the HTTP DATE header
// Fetch the page over JS to get just the headers
console.log("syncing time")
var r = new XMLHttpRequest();
var start = (new Date).getTime();
r.open(''HEAD'', document.location, false);
r.onreadystatechange = function()
{
if (r.readyState != 4)
{
return;
}
var latency = (new Date).getTime() - start;
var timestring = r.getResponseHeader("DATE");
// Set the time to the **slightly old** date sent from the
// server, then adjust it to a good estimate of what the
// server time is **right now**.
systemtime = new Date(timestring);
systemtime.setMilliseconds(systemtime.getMilliseconds() + (latency / 2))
};
r.send(null);
}
Interesante aparte : John Resig tiene un buen artículo sobre la precisión del tiempo de Javascript.
No debería causar un problema en este caso, ya que solo le preocupa que su tiempo se apague en ~ 1 segundo. Una diferencia de 15 ms no debería tener mucho efecto.
Realmente no puedes sincronizar con el servidor. Medir el tiempo que necesita su servidor (como sugirió MikeWyatt) no es un buen indicador de la latencia.
Solo su servidor sabe cuándo responde una solicitud. Por lo tanto, debe enviar esa información con la respuesta. Con Date.now() - new Date(timestringOfServerResponse)
puede medir exactamente la latencia. Sin embargo, no estoy seguro de por qué necesitarías ese valor.
Para sincronizar una aplicación entre múltiples dispositivos, el servidor debe enviarles qué acción realizar cuando. El "cuándo" no debería ser "tan pronto como reciba mi respuesta", sino una marca de tiempo exacta. En cuanto a que los relojes del sistema de sus dispositivos son precisos y sincronizados (generalmente lo son), la aplicación ejecutará sus métodos de forma sincronizada, porque sabe qué suceder cuando (o al menos: lo que debería haber sucedido en ese momento, y puede interpolar suceder "ahora").