简体   繁体   English

requestAnimationFrame [now] vs performance.now()时间差异

[英]requestAnimationFrame [now] vs performance.now() time discrepancy

Assumptions: rAF now time is calculated at the time the set of callbacks are all triggered. 假设:rAF now时间是在所有回调都被触发时计算的。 Therefore any blocking that happens before the first callback of that frame is called doesn't affect the rAF now and it's accurate--at least for that first callback. 因此,在调用该帧的第一个回调之前发生的任何阻塞now都不会影响rAF并且它是准确的 - 至少对于第一次回调而言。

Any performance.now() measurements made before a rAF set is triggered should be earlier than rAF now . 在触发rAF集之前进行的任何performance.now()测量应该早于now rAF。

Test: Record before (a baseline time before anything happens). 测试:记录before任何事情发生before的基线时间)。 Set the next rAF. 设置下一个rAF。 Compare rAF now and actual performance.now() to before to see how different they are. now比较rAF和实际performance.now() before看看它们有多么不同。

Expected results: 预期成绩:

 var before = performance.now(), frames = ["with blocking", "with no blocking"], calls = 0; requestAnimationFrame(function frame(rAFnow) { var actual = performance.now(); console.log("frame " + (calls + 1) + " " + frames[calls] + ":"); console.log("before frame -> rAF now: " + (rAFnow - before)); console.log("before frame -> rAF actual: " + (actual - before)); if (++calls < frames.length) { before = actual; requestAnimationFrame(frame); } }); // blocking for (var i = 0, l = 0; i < 10000000; i++) { l += i; } 

Observations: When there is blocking before the frame starts, the rAF now time is at times incorrect, even for that first frame. 观察:当帧开始之前存在阻塞时,即使对于第一帧,rAF now时间有时也是不正确的。 Sometimes the first frame's now is actually an earlier time than the recorded before time. 有时,第一帧now实际上是比before记录的时间更早的时间。

Whether there is blocking happening before the frame or not, every so often the in-frame time rAFnow will be earlier than the pre-frame time before --even when I setup the rAF after I take my first measurement. 无论是否在帧之前发生阻塞,帧内时间rAFnow都会比之前的帧before时间before我进行第一次测量设置rAF时。 This can also happen without any blocking whatsoever, though this is rarer. 这也可以在没有任何阻塞的情况下发生,尽管这种情况很少见。

(I get a timing error on the first blocking frame most of the time. Getting an issue on the others is rarer, but does happen occasionally if you try running it a few times.) (我在大多数时间都在第一个阻塞帧上出现时间错误。在其他阻塞帧上出现问题的情况比较少见,但如果你尝试运行几次,偶尔会发生这种情况。)

With more extensive testing, I've found bad times with blocking prior to callback: 1% from 100 frames, no blocking: 0.21645021645021645% from ~400 frames, seemingly caused by opening a window or some other potentially CPU-intensive action by the user. 通过更广泛的测试,我发现在回调之前阻塞的不良时间:100帧中的1%,没有阻塞:来自~400帧的0.21645021645021645%,似乎是由用户打开窗口或其他一些潜在的CPU密集型操作引起的。

So it's fairly rare, but the problem is this shouldn't happen at all. 所以这是相当罕见的,但问题是这根本不会发生。 If you want to do useful things with them, simulating time, animation, etc., then you need those times to make sense. 如果你想用它们做有用的事情,模拟时间,动画等,那么你需要那些时间才有意义。

I've taken into account what people have said, but maybe I am still not understanding how things work. 我已经考虑到了人们所说的话,但也许我仍然不理解事情的运作方式。 If this is all per-spec, I'd love some psuedo-code to solidify it in my mind. 如果这是所有的规格,我会喜欢一些伪代码来巩固它在我的脑海里。

And more importantly, if anyone has any suggestions for how I could get around these issues, that would be awesome. 更重要的是,如果有人对如何解决这些问题有任何建议,那将是非常棒的。 The only thing I can think of is taking my own performance.now() measurement every frame and using that--but it seems a bit wasteful, having it effectively run twice every frame, on top of any triggered events and so on. 我唯一能想到的是自己的performance.now()测量每一帧并使用它 - 但它看起来有点浪费,让它在每一帧上有效地运行两次,在任何触发事件之上等等。

The timestamp passed in to the requestAnimationFrame() callback is the time of the beginning of the animation frame. 传递给requestAnimationFrame()回调的时间戳是动画帧开始的时间。 Multiple callbacks being invoked during the same frame all receive the same timestamp. 在同一帧期间调用的多个回调都接收相同的时间戳。 Thus, it would be really weird if performance.now() returned a time before the parameter value, but not really weird for it to be after that. 因此,如果performance.now()在参数值之前返回一个时间,那将是非常奇怪的,但之后它并不是真的很奇怪。

Here's the relevant specification: 这是相关规范:

When the user agent is to run the animation frame callbacks for a Document document with a timestamp now , it must run the following steps: 当用户代理是一种用于与时间戳的文档文件现在运行的动画帧的回调,它必须执行下列步骤:

  1. If the value returned by the document object's hidden attribute is true, abort these steps. 如果文档对象的hidden属性返回的值为true,则中止这些步骤。 [PAGE-VISIBILITY] [PAGE-VISIBILITY]

  2. Let callbacks be a list of the entries in document's list of animation frame callbacks, in the order in which they were added to the list. 让回调成为文档动画帧回调列表中的条目列表,按照它们添加到列表中的顺序排列。

  3. Set document's list of animation frame callbacks to the empty list. 将文档的动画帧回调列表设置为空列表。

  4. For each entry in callbacks, in order: invoke the Web IDL callback function, passing now as the only argument, and if an exception is thrown, report the exception. 对于回调中的每个条目,按顺序:调用Web IDL回调函数, 现在作为唯一参数传递,如果抛出异常,则报告异常。

So you've registered a callback (let's say just one) for the next animation frame. 所以你已经为下一个动画帧注册了一个回调(假设只有一个)。 Tick tick tick, BOOM, time for that animation frame to happen: 勾选滴答滴答,BOOM,该动画帧的发生时间:

  1. The JavaScript runtime makes a note of the time and labels that now . JavaScript运行时会记录现在的时间和标签。
  2. The runtime makes a temporary copy of the list of registered animation frame callbacks, and clears the actual list (so that they're not accidentally invoked if things take so long that the next animation frame comes around). 运行时生成已注册动画帧回调列表的临时副本,并清除实际列表(如果事情需要很长时间以至于下一个动画帧出现,则不会意外调用它们)。
  3. The list has just one thing in it: your callback. 列表中只有一件事:你的回调。 The system invokes that with now as the parameter. 系统使用now作为参数调用它。
  4. Your callback starts running. 你的回调开始运行。 Maybe it's never been run before, so the JavaScript optimizer may need to do some work. 也许它以前从未运行过,所以JavaScript优化器可能需要做一些工作。 Or maybe the operating system switches threads to some other system process, like starting up a disk buffer flush or handling some network traffic, or any of dozens of other things. 或者操作系统可能会将线程切换到某些其他系统进程,例如启动磁盘缓冲区刷新或处理某些网络流量,或任何其他任何系统进程。
  5. Oh right, your callback. 哦,对,你的回调。 The browser gets the CPU again and your callback code starts running. 浏览器再次获取CPU并且您的回调代码开始运行。
  6. Your code calls performance.now() and compares it to the now value passed in as a parameter. 您的代码调用performance.now()并将其与作为参数传入的now值进行比较。

Because a brief but non-ignorable amount of time may pass between step 1 and step 6, the return value from performance.now() may indicate that a couple of microseconds, or even more than a couple, have elapsed. 因为在步骤1和步骤6之间可能会经过一段短暂但不可忽略的时间,所以performance.now()的返回值可能表示已经过了几微秒,甚至多于几微秒。 That is perfectly normal behavior. 这是完全正常的行为。

I encountered the same issue on chrome, where calls to performance.now () would return a higher value than the now value passed into subsequent callbacks made by window.requestAnimationFrame () 我在chrome上遇到了同样的问题,其中对performance.now ()调用返回的值高于传递给window.requestAnimationFrame ()后续回调的now值。

My workaround was to set the before using the now passed to the callback in the first window.requestAnimationFrame () rather than performance.now () . 我的解决方法是在第一个window.requestAnimationFrame () 不是performance.now ()设置before使用now传递给回调的。 It seems that using only one of the two functions to measure time guarantees progressing values. 似乎使用两个函数中的一个来测量时间可以保证值的进步。

I hope this helps anyone else suffering through this bug. 我希望这可以帮助其他任何遭受这个bug的人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM