在多个设备之间同步JS时间

Jos*_*unt 23 javascript time synchronization

我正在使用精彩的reveal.js库来创建HTML幻灯片.我唯一的问题是我需要它来跨多个设备进行同步.

目前我正在从服务器发出一个AJAX请求,并保留页面的内部时钟.

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
}
Run Code Online (Sandbox Code Playgroud)

虽然这让我在1秒左右的准确度,但我需要做得更好.幻灯片显示自动前进时,差异非常明显.

代码将在同一平台上运行,因此跨浏览器兼容性无需担心.

这就是我设法组建的内容

有任何想法吗?

Mik*_*att 22

测量发送请求和返回响应之间经过的时间.然后,将该值除以2.这为您提供了单向延迟的粗略值.如果将其添加到服务器的时间值,您将更接近真实的服务器时间.

这样的事情应该有效:

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);
}
Run Code Online (Sandbox Code Playgroud)

有趣的是:John Resig有一篇关于Javascript计时准确性的文章.
在这种情况下它不应该引起问题,因为你只关心你的休息时间约1秒钟.15毫秒的差异不应该有太大的影响.

  • 我就是这样做的.唯一的区别是我会多次进行同步调用(即10次),然后使用具有最低延迟的调用的系统时间.实际上,延迟越高,从客户端到服务器时间和从服务器到客户端时间之间的分配不完全是1:1的事实就会产生的影响越大. (4认同)

jos*_*736 19

不同的方法怎么样:谁在乎时间?(您不会使用JavaScript可靠地同步系统时钟.)

而是在客户端推进幻灯片放映时使用带有socket.io节点服务器进行同步.服务器告诉他们,而不是客户决定何时前进.

这种方法带来了额外的好处,即能够在幻灯片运行时手动摆弄幻灯片.在下面的示例中,我添加了一个Next按钮,使所有连接的客户端立即前进到下一张幻灯片.

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);
}
Run Code Online (Sandbox Code Playgroud)

意见/ index.html的

此文件作为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 &gt;</button>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

将这两个文件复制到一个文件夹中,然后运行

$ npm install express socket.io dot
$ node app.js
Run Code Online (Sandbox Code Playgroud)

并导航到http://localhost:70几个不同的窗口,然后看到魔术.


Dav*_*aun 11

我很高兴你找到了一个满意的答案.我有类似的需要将浏览器与服务器的时钟同步,并决心以超过1秒的精度实现它,就像你一样.我已编写代码来执行此操作,并在此处发布此答案,以防其他人也需要解决方案.

该代码称为ServerDate,可免费下载.这是自述文件的一部分.请注意,在我的示例中,我达到了108 ms的精度:

您可以ServerDate像使用Date函数或其中一个实例一样使用,例如:

> ServerDate()
"Mon Aug 13 2012 20:26:34 GMT-0300 (ART)"

> ServerDate.now()
1344900478753

> ServerDate.getMilliseconds()
22
Run Code Online (Sandbox Code Playgroud)

还有一种新方法可以获得ServerDate估计服务器时钟的精度(以毫秒为单位):

> ServerDate.toLocaleString() + " ± " + ServerDate.getPrecision() + " ms"
"Tue Aug 14 01:01:49 2012 ± 108 ms"
Run Code Online (Sandbox Code Playgroud)

您可以看到服务器的时钟和浏览器时钟之间的差异,以毫秒为单位:

> ServerDate - new Date()
39
Run Code Online (Sandbox Code Playgroud)