JavaScript按键时间测量

3

我正在处理一个非常时间紧迫的应用程序,它使用按键作为用户输入。因为毫秒级别的问题很重要,所以我尝试了以下版本:

function start() {
  //stim.style.display = "block";
  rt_start = new Date().getTime();
  response_allowed = 1;
}

function end() {
  var t = rt_end - rt_start;
  //stim.style.display = "none";
  log.innerHTML = t;
  i++;
  if (i < iterations) {
    setTimeout('start();', 1000);
  }
}

var rt_start;
var rt_end;
var iterations = 100;
var i = 0;
var response_allowed = 0;
var stim;
var log;
$(document).ready(function() {
  document.onkeydown = function(e) {
    if (response_allowed == 1) {
      rt_end = new Date().getTime();
      response_allowed = 0;
      end();
    }
  };
  stim = document.getElementById('stim');
  log = document.getElementById('log');
  start();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="log"></div>
<img src="https://www.gravatar.com/avatar/cfefd93404e6b0eb3cde02b4b6df4e2b?s=128&d=identicon&r=PG&f=1" id="stim" />

这段代码在普通情况下运行良好,计时器通常不到5ms(只需按住一个键即可)。但一旦我修改代码以显示图像(取消注释两行),它的速度就会大大降低,约为30ms。

有人能告诉我为什么会出现这种情况以及如何可能避免这种额外的延迟吗?

谢谢


你能提供一个 jsfiddle 吗? - artm
附注:几乎没有理由将字符串传递给 setTimeout/setInterval,而且有几个原因不这样做。相反,传递要调用的函数的引用:setTimeout(start, 1000); - T.J. Crowder
jsfiddle似乎由于setTimeout而无法工作?至少我之前无法让它工作,抱歉。传递字符串是因为我的方法是在原始脚本中传递多个函数/操作,例如显示div,更改其内容等。这些是链接在一起的各种操作。编辑:这些操作高度可变,因此没有办法为每个操作定义一个定义的函数。 - Karsten Sender
@KarstenSender:setTimeout在jsFiddle上运行良好(http://jsfiddle.net/gcvtaxfm/),但我已经将您的代码更新为Stack Snippet(我使用了您的gravatar而不是test.png)。 - T.J. Crowder
说实话,使用上面的版本,按住一个键会让我得到约23毫秒范围内的值。(按住键意味着我们在等待按键重复,对吗?) - T.J. Crowder
@T.J.Crowder 谢谢! - Karsten Sender
1个回答

1
我建议在可用的情况下使用DOMHighResTimeStamp(对于不提供该功能的浏览器,使用polyfill)。
这是一个高分辨率时间戳(设计用于准确测量),可与导航计时和Web性能API一起使用(在Mozilla开发者网络中搜索此内容,因为我不能在单个帖子中分享超过两个链接)。
获取DOMHighResTimeStamp的快速方法-就像使用var ts = new Date().getTime();获取常规毫秒级时间戳一样-是: var ts = performance.now(); 如我上面所说,请查看MDN上的Web性能API。如果您的应用程序确实非常时间敏感,则非常有帮助。
编辑:
关于你的代码片段,我认为如果你按住一个键,则始终受限于keydown事件的分辨率(该事件连续触发,但不是每毫秒一次)。如果你在文本编辑器中连续按下字符键并检查每秒钟字符被写入的次数,你可以轻松看到这种行为。我猜这是通过操作系统设置控制的。
你还受到setTimeout/setInterval相关的“漂移”限制。你要知道,setTimeout将某个东西排队等待在给定延迟后执行,但它不能保证及时执行。这是一个“尽力而为”的情况,如果浏览器正在忙于做某些事情,它将会明显地漂移。也就是说,如果你使用setTimeout在1秒后重新启用response_allowed变量,你可以期望它在“大约”(但不完全)1秒后重新启用。

嗨,感谢您的回答。我已经测试了performance.now(),但它似乎比Date.now()引起更高的负载(请参见http://jsperf.com/date-now-vs-new-date-gettime/34)。setTimeout的延迟在这里不是问题,因为我只在函数内开始计时-因此忽略迭代之间的(多或少)1000ms。正如@TJCrowder所提到的,更大的问题可能是keydown事件中存在延迟。通过一个名为Keydrown的小应用程序(http://jeremyckahn.github.io/keydrown/)已经(在某种程度上)确认和修复了这个问题。谢谢! - Karsten Sender

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接