如何在 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 帧/秒?显示器可以吗?性能影响如何?