我试图用Javascript制作一个睡眠函数。
这个函数 drawLinesToHtmlCanvas()
的目的是为了在HTML画布上绘制随机的线条,而用户则是为了能够看到实时绘制的线条。
在这个例子中,我使用了500毫秒的延迟,但我希望能够达到1毫秒(甚至在将来低于1毫秒的分辨率)。
本来我是按照这个帖子的答案来的。sleep()的JavaScript版本是什么?
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function drawLinesToHtmlCanvas() {
// Get canvas and context here...
var drawSpeed = 500; // ms.
for (i=0; i<lines; i++) {
// Draw lines to canvas...
await sleep(drawSpeed);
}
}
而且效果非常好(上图)。它的效率很高,一点也不拖累浏览器,而且让我对时间有一定的控制。
问题是 setTimeout()
似乎不能精确到1ms,而这是我对这个函数的要求。
所以我反而尝试了自己的方法,如下。
function sleep(ms) {
ms = parseInt(ms);
var now = new Date();
nowMs = now.valueOf();
var endMs = nowMs + ms;
while (endMs > nowMs) {
nowMs = new Date().valueOf();
}
return true;
}
function drawLinesToHtmlCanvas() {
// Get canvas and context here...
var drawSpeed = 500; // ms.
for (i=0; i<lines; i++) {
// Draw lines to canvas...
while (!sleep(drawSpeed));
}
}
这个方法非常慢,等待正确时间的while循环耗尽了所有浏览器的资源,完全无法使用。另外由于这个函数 drawLinesToHtmlCanvas()
运行时,线条没有被更新到画布元素。
的承诺解决方案。setTimeout()
是很棒的,只是对我的要求不够精确。
我可以做一个类似第一个例子的承诺吗?但是不使用 setTimeout()
它使用的算法与我的 Date()
现在与终点毫秒的对比,因为这样会更准确?
线条暂时需要能够画到1毫秒,并且有实时更新,用户需要能够看到线条被画到画布上。
即使 setTimeout
在这种极小的时间框架上工作,这可能不会成功。当你使用回调或承诺时,你依赖于JS运行时的事件循环。这个事件循环只会以最快的速度执行你的回调。架构会施加滞后,当你低于1ms时,滞后会变得明显。回调在 setTimeout
在N毫秒过后,并不完全执行。N ms过后,它才有资格被执行。而它只有在轮到它的另一个事件循环tick时才会最终被调用。
至于你的第二种方法,它不完全是 "占用资源"。问题是你不再使用事件循环。但是你必须记住JS是单线程的。正因为如此,当JS代码不停地执行时,它根本不会让用户与UI交互。用户只能做一些事情 之间 事件回调执行。所以千万不要在浏览器中使用JS中的长运行whiles,除非你想破坏用户体验。也许除非你使用Web worker,因为它们会让你创建新的线程,但那样你就无法从那里画出任何东西。
总的来说,你把动画看成是 "画点东西然后睡觉 "的做法是相当幼稚的。虽然在浏览器中编写动画以有效地利用视频卡可能会很棘手,但执行和流畅的动画是视频卡的目的。如果你想在浏览器中制作动画,那么你必须找到专门为Canvas或WebGL上的动画制作的浏览器函数调用。https:/developer.mozilla.orgen-USdocsWebAPICanvas_APITutorialBasic_animations。
同时考虑一下,如果你真的需要如此可能的帧每秒。超过1000fps?显示器能做到吗?性能影响如何?