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