如何在 Javascript 中使用 promises 实现精确的睡眠功能?
How to make a precise sleep function in Javascript, possibly using promises?
我正在尝试在 Javascript 中实现睡眠功能。
函数 drawLinesToHtmlCanvas()
用于将随机线条绘制到 HTML canvas 并且用户能够实时看到正在绘制的线条。
对于这个例子,我使用了 500 毫秒的延迟,但希望能够达到 1 毫秒(或者将来甚至小于 1 毫秒的分辨率)
最初我遵循了这个 post 的答案:What is the JavaScript version of sleep()?
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()
似乎无法降低到 1 毫秒的精度,这是我对此功能的要求。
所以我尝试了自己的方法如下:
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()
是 运行,这些行不会更新到 canvas 元素。
setTimeout()
的 promise 解决方案很棒,只是不够精确,无法满足我的要求。
我可以做出类似于第一个示例的承诺吗?但是它没有使用 setTimeout()
,而是使用了与我的 Date()
now vs end ms 比较类似的算法,因为那样会更准确吗?
现在需要能够将线条绘制到 1 毫秒并进行实时更新,用户需要能够看到绘制到 canvas.[=20= 的线条]
即使 setTimeout
在如此短的时间范围内工作,这也可能不会成功。当你使用回调时 and/or 承诺你依赖 JS 运行时的事件循环。这个事件循环只会尽可能快地执行你的回调。该架构将施加延迟,当您低于 1 毫秒时,这些延迟将变得可见。 setTimeout
中的回调在经过 N ms 后并未准确执行。经过 N ms 后,它才有资格被执行。并且只有在轮到另一个事件循环 tick 时才会最终调用它。
至于您的第二种方法,它并不完全 "use up resources"。问题是您不再使用事件循环。但是你必须记住,JS 是单线程的。正因为如此,当 JS 代码不停地执行时,它根本不会让用户与 UI 进行交互。用户只能在 事件回调执行之间做一些事情。因此,除非您想破坏用户体验,否则永远不要在浏览器的 JS 中使用 long 运行 while。也许除非你使用 Web worker,因为它们会让你创建新线程,但你将无法从那里绘制任何东西。
总的来说,您作为 "drawing something and then sleeping" 的动画方法相当幼稚。尽管在浏览器中编写动画以有效利用视频卡可能很棘手,但制作视频卡的目的是实现高性能和流畅的动画。如果你想在浏览器中制作动画,那么你必须找到专门为 Canvas 或 WebGL 上的动画制作的特定浏览器函数调用。
也许从这里开始:
https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_animations
还要考虑一下您是否真的需要每秒可能的帧数。超过 1000 帧/秒?显示器可以吗?性能影响如何?
我正在尝试在 Javascript 中实现睡眠功能。
函数 drawLinesToHtmlCanvas()
用于将随机线条绘制到 HTML canvas 并且用户能够实时看到正在绘制的线条。
对于这个例子,我使用了 500 毫秒的延迟,但希望能够达到 1 毫秒(或者将来甚至小于 1 毫秒的分辨率)
最初我遵循了这个 post 的答案:What is the JavaScript version of sleep()?
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()
似乎无法降低到 1 毫秒的精度,这是我对此功能的要求。
所以我尝试了自己的方法如下:
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()
是 运行,这些行不会更新到 canvas 元素。
setTimeout()
的 promise 解决方案很棒,只是不够精确,无法满足我的要求。
我可以做出类似于第一个示例的承诺吗?但是它没有使用 setTimeout()
,而是使用了与我的 Date()
now vs end ms 比较类似的算法,因为那样会更准确吗?
现在需要能够将线条绘制到 1 毫秒并进行实时更新,用户需要能够看到绘制到 canvas.[=20= 的线条]
即使 setTimeout
在如此短的时间范围内工作,这也可能不会成功。当你使用回调时 and/or 承诺你依赖 JS 运行时的事件循环。这个事件循环只会尽可能快地执行你的回调。该架构将施加延迟,当您低于 1 毫秒时,这些延迟将变得可见。 setTimeout
中的回调在经过 N ms 后并未准确执行。经过 N ms 后,它才有资格被执行。并且只有在轮到另一个事件循环 tick 时才会最终调用它。
至于您的第二种方法,它并不完全 "use up resources"。问题是您不再使用事件循环。但是你必须记住,JS 是单线程的。正因为如此,当 JS 代码不停地执行时,它根本不会让用户与 UI 进行交互。用户只能在 事件回调执行之间做一些事情。因此,除非您想破坏用户体验,否则永远不要在浏览器的 JS 中使用 long 运行 while。也许除非你使用 Web worker,因为它们会让你创建新线程,但你将无法从那里绘制任何东西。
总的来说,您作为 "drawing something and then sleeping" 的动画方法相当幼稚。尽管在浏览器中编写动画以有效利用视频卡可能很棘手,但制作视频卡的目的是实现高性能和流畅的动画。如果你想在浏览器中制作动画,那么你必须找到专门为 Canvas 或 WebGL 上的动画制作的特定浏览器函数调用。 也许从这里开始: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_animations
还要考虑一下您是否真的需要每秒可能的帧数。超过 1000 帧/秒?显示器可以吗?性能影响如何?