Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/378.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何在Firebase中实现分布式倒计时_Javascript_Firebase Realtime Database - Fatal编程技术网

Javascript 如何在Firebase中实现分布式倒计时

Javascript 如何在Firebase中实现分布式倒计时,javascript,firebase-realtime-database,Javascript,Firebase Realtime Database,我有一个应用程序,其中一个用户主持一个游戏,然后其他用户可以就主持人提出的问题进行投票。从主持人发帖的那一刻起,球员们有20秒的时间投票 如何在所有玩家的屏幕上显示倒计时计时器,并使其与主机保持同步?许多开发人员都陷入了这个问题,因为他们试图在所有用户之间同步倒计时本身。这很难保持同步,而且容易出错。然而,有一种更简单的方法,我在许多项目中都使用过 每个客户端都需要显示其倒计时计时器,这是三个相当静态的信息: 发布问题的时间,即计时器启动的时间 他们需要从那一刻开始计算的时间 客户端到中央计时器

我有一个应用程序,其中一个用户主持一个游戏,然后其他用户可以就主持人提出的问题进行投票。从主持人发帖的那一刻起,球员们有20秒的时间投票


如何在所有玩家的屏幕上显示倒计时计时器,并使其与主机保持同步?

许多开发人员都陷入了这个问题,因为他们试图在所有用户之间同步倒计时本身。这很难保持同步,而且容易出错。然而,有一种更简单的方法,我在许多项目中都使用过

每个客户端都需要显示其倒计时计时器,这是三个相当静态的信息:

  • 发布问题的时间,即计时器启动的时间
  • 他们需要从那一刻开始计算的时间
  • 客户端到中央计时器的相对偏移量
  • 我们将使用数据库的服务器时间作为第一个值,第二个值来自hosts代码,这是Firebase为我们提供的值

    下面的代码示例是用JavaScript为web编写的,但采用了相同的方法(以及非常类似的代码),并应用于iOS、Android和大多数其他实现实时侦听器的Firebase SDK


    让我们首先将开始时间和间隔写入数据库。忽略安全规则和验证,这可以简单到:

    const database = firebase.database();
    const ref = database.ref("countdown");
    ref.set({
      startAt: ServerValue.TIMESTAMP,
      seconds: 20
    });
    
    当我们执行上述代码时,它将当前时间写入数据库,这是一个20秒的倒计时。由于我们使用
    ServerValue.TIMESTAMP
    写入时间,数据库将在服务器上写入时间,因此不可能受主机本地时间(或偏移量)的影响


    现在让我们看看其他用户的如何读取该数据。与Firebase一样,我们将使用
    on()
    侦听器,这意味着我们的代码在写入数据时正在积极侦听:

    ref.on("value", (snapshot) => {
      ...
    });
    
    当执行此
    ref.on(…
    代码时,它会立即从数据库中读取当前值并运行回调。但它也会继续侦听数据库的更改,并在发生另一次写入时再次运行代码

    因此,让我们假设我们正在使用一个新的数据快照调用刚刚开始的倒计时。我们如何在所有屏幕上显示一个准确的倒计时计时器

    我们将首先使用以下命令从数据库中获取值:

    ref.on("value", (snapshot) => {
      const seconds = snapshot.val().seconds;
      const startAt = snapshot.val().startAt;
      ...
    });
    
    我们还需要估计本地客户端和服务器之间的时间。Firebase SDK在第一次连接到服务器时估计这段时间,我们可以从客户端的
    .info/serverTimeOffset
    读取它:

    const serverTimeOffset = 0;
    database.ref(".info/serverTimeOffset").on("value", (snapshot) => { serverTimeOffset = snapshot.val() });
    ref.on("value", (snapshot) => {
      const seconds = snapshot.val().seconds;
      const startAt = snapshot.val().startAt;
      
    });
    
    在运行良好的系统中,
    serverTimeOffset
    是一个正值,表示我们对服务器的延迟(以毫秒为单位)。但如果本地时钟有偏移量,它也可能是负值。无论哪种方式,我们都可以使用此值来显示更准确的倒计时计时器

    接下来,我们将启动一个间隔计时器,它大约每100毫秒接收一次呼叫:

    const serverTimeOffset = 0;
    database.ref(".info/serverTimeOffset").on("value", (snapshot) => { serverTimeOffset = snapshot.val() });
    ref.on("value", (snapshot) => {
      const seconds = snapshot.val().seconds;
      const startAt = snapshot.val().startAt;
      const interval = setInterval(() => {
        ...
      }, 100)
    });
    
    然后,每一个计时器我们的间隔到期,我们将计算剩余的时间:

    const serverTimeOffset = 0;
    database.ref(".info/serverTimeOffset").on("value", (snapshot) => { serverTimeOffset = snapshot.val() });
    ref.on("value", (snapshot) => {
      const seconds = snapshot.val().seconds;
      const startAt = snapshot.val().startAt;
      const interval = setInterval(() => {
        const timeLeft = (seconds * 1000) - (Date.now() - startAt - serverTimeOffset);
        ...
      }, 100)
    });
    
    最后,我们以合理的格式记录剩余时间,并在计时器过期时停止计时器:

    const serverTimeOffset = 0;
    database.ref(".info/serverTimeOffset").on("value", (snapshot) => { serverTimeOffset = snapshot.val() });
    ref.on("value", (snapshot) => {
      const seconds = snapshot.val().seconds;
      const startAt = snapshot.val().startAt;
      const interval = setInterval(() => {
        const timeLeft = (seconds * 1000) - (Date.now() - startAt - serverTimeOffset);
        if (timeLeft < 0) {
          clearInterval(interval);
          console.log("0.0 left)";
        }
        else {
          console.log(`${Math.floor(timeLeft/1000)}.${timeLeft % 1000}`);
        }
      }, 100)
    });
    
    const serverTimeOffset=0;
    database.ref(“.info/serverTimeOffset”).on(“value”,(snapshot)=>{serverTimeOffset=snapshot.val()});
    参考on(“值”,(快照)=>{
    const seconds=snapshot.val().seconds;
    const startAt=snapshot.val().startAt;
    常量间隔=设置间隔(()=>{
    常量timeLeft=(秒*1000)-(Date.now()-startAt-serverTimeOffset);
    如果(时间间隔<0){
    间隔时间;
    控制台日志(“0.0左)”;
    }
    否则{
    log(${Math.floor(timeLeft/1000)}.${timeLeft%1000});
    }
    }, 100)
    });
    

    在上面的代码中肯定还有一些清理工作要做,例如,当一个新的倒计时开始时,一个仍在进行中,但总体方法运行良好,可以轻松扩展到数千个用户。

    您使用的是“Firebase”与“Firebase实时数据库”的同义词.我知道这曾经是准确的-我想知道:这在今天仍然有意义吗?我用
    firebase realtime database
    标记了它_(ツ)_/“我发布此问答是因为我刚刚在一个应用程序中实现了这样一个倒计时,并意识到其他人可能会从中受益。如果你有一个你认为与今天更相关的备选答案,请在下面发布。很好的解释!不过我有一个问题,是on()listener是否等于Java中的ValueEventListener?是的,上的
    映射到
    addValueListener
    一次将映射到
    addListenerForSingleValueEvent
    (或
    get
    )。