Node.js 制作实时多人游戏框架

2020-06-17 06:55:03易采站长站整理

于是我们马上又想到了使用 setTimeout(),通过动态地修正下一次到时的时间来让我们的逻辑大致稳定在规定的 interval 左右。例如此次setTimeout()比预期少了5ms, 那么我们下一次就让他提前5ms. 不过测试结果不尽人意,而且这怎么看都不够优雅。

所以我们又要换一个思路。是否可以让 setTimeout() 尽可能快地到期,然后我们检查当前的时间是否到达目标时间。例如在我们的循环中,使用setTimeout(callback, 1) 来不停地检查时间,这看起来像是一个不错的主意。

令人失望的计时器
我们立即写了一段代码来测试我们的想法,结果令人失望。在目前最新的node.js 稳定版(v0.10.32)以及 Windows 平台下,运行这样一段代码:


var sum = 0, count = 0; 
function test() { 
  var now = Date.now(); 
  setTimeout(function () { 
    var diff = Date.now() – now; 
    sum += diff; 
    count++; 
    test(); 
  }); 

test(); 

一段时间之后在控制台里输入 sum/count,可以看到一个结果,类似于:


> sum / count 
15.624555160142348 

什么?!!我要 1ms 的间隔时间,你却告诉我实际的平均间隔为 15.625ms!这个画面简直是太美。我们在 mac 上做同样的测试,得到的结果是 1.4ms。于是我们心生疑惑:这到底是什么鬼?如果我是一个果粉,我可能就要得出 Windows 太垃圾然后放弃 Windows 的结论了,不过好在我是一名严谨的前端工程师,于是我开始继续思索起这个数字来。

等等,这个数字为什么那么眼熟?15.625ms 这个数字会不会太像 Windows 下的最大计时器间隔了?立即下载了一个 ClockRes 进行测试,控制台一跑果然得到了如下结果:


Maximum timer interval: 15.625 ms 
Minimum timer interval: 0.500 ms 
Current timer interval: 1.001 ms 

果不其然!查阅 node.js 的手册我们能看到这样一段对 setTimeout 的描述:
 
The actual delay depends on external factors like OS timer granularity and system load.
然而测试结果显示,这个实际延迟是最大计时器间隔(注意此时系统的当前计时器间隔只有 1.001ms),无论如何让人无法接受,强大的好奇心驱使我们翻翻看 node.js 的源码来一窥究竟。

Node.js 中的 BUG

相信大部分你我都对 Node.js 的 even loop 机制有一定的了解,查看 timer 实现的源码我们可以大致了解到 timer 的实现原理,让我们从 event loop 的主循环讲起:
 

while (r != 0 && loop->stop_flag == 0) { 
    /* 更新全局时间 */