了解 Javascript canvas 中的异步 setInterval

Understanding Javascript asynchronous setInterval in canvas

当使用 setInterval 调用 init() 方法时,以下代码创建了一个 canvas,球位于中心。当触发 keydown() 事件时,球会在 X 轴和 Y 轴上移动(取决于用户按下的键)。我很难理解的是当按下一个键时会发生什么。

当按下键更新键值(keyLeftkeyRight 等)时,setInterval 是否停止,然后恢复并考虑更新的值

按键事件中的代码是否在 setInterval 进行时执行?

我读到 setInterval 是异步的,而关键事件是同步的,并且由于 Javascript 是单线程的,所以第二个选项不可能是真的,对吧?我真的可以在这方面使用你的知识..

    <canvas id="canvas" style="border: 1px solid black" width="400" height="400"></canvas>
    <script type="text/javascript" src="jquery.js"></script>
    <script type="text/javascript">
        var Ball = {
            positionX: 200,
            positionY: 200,
            keyLeft: false,
            keyRight: false,
            keyUp: false,
            keyDown: false,
            init: function() {
                this.canvas = document.getElementById('canvas');
                this.ctx = this.canvas.getContext('2d');
                setInterval(Ball.draw, 10);
            },
            circle: function (x, y, fill) {
                this.ctx.beginPath();
                this.ctx.arc(x, y, 20, 0, Math.PI * 2, false);
                if (fill) {
                    this.ctx.fill();
                } else {
                    this.ctx.stroke();
                }
            },
            draw: function() {
                Ball.ctx.clearRect(0, 0, 400, 400);
                if (Ball.keyLeft) Ball.positionX -= 5;
                else if (Ball.keyRight) Ball.positionX += 5;
                if (Ball.keyUp) Ball.positionY -= 5;
                else if (Ball.keyDown) Ball.positionY += 5;
                Ball.circle(Ball.positionX, Ball.positionY, true);
            }
        }
        Ball.init();

        $('body').keydown(function (e) {
            var key = e.keyCode;
            if (key == 37) {
                Ball.keyLeft = true;
            } else if (key == 39) {
                Ball.keyRight = true;
            }

            if (key == 38) {
                Ball.keyUp = true;
            } else if (key == 40) {
                Ball.keyDown = true;
            }
        });
    </script>

Javascript(在浏览器中)是一种事件驱动的语言。大多数时候,什么都没有执行。当一个事件发生并且脚本已经为该特定类型的事件注册了一个侦听器时,该事件将被放入一个队列中。如果脚本的某些部分当前正在执行,它不会被中断 - 相反,当它完成时,浏览器将从队列中拉出下一个事件并开始为它执行注册的处理程序。

在您的代码中,您正在注册两种事件:计时器事件(隐式地,使用 setInterval)和 keydown 事件(显式地,使用 jQuery,它使用 addEventListener).当计时器事件触发时,您的 Ball.draw 方法将被执行。如果 keydown 事件在 运行 时到达,它将在队列中等待直到 Ball.draw 完成。然后它被分派到您分配的处理函数。反之亦然:如果 keydown 处理程序正在进行中,经过 10 毫秒,并且发生计时器事件,则计时器事件必须等到 keydown 处理程序完成。这就是 Javascript 计时器本质上不精确的原因之一。

简短版本:您的 keydown 处理程序可以在 Ball.draw 的执行之间执行。它对两个函数使用的变量所做的任何更改都将在下次调用时对 Ball.draw 可见。

setInterval 方法为将来的执行安排了一个事件,但在时间过去并调用该方法之前不会阻塞。 JS 将在方法调用期间阻塞,并在方法调用完成时停止阻塞。

按照这段代码的编写方式,每 10 毫秒将调用绘制方法(这比浏览器渲染速度更快,即 60 fps 或大约每 16 毫秒)。如果自上次调用后发生了 keydown 事件,draw 方法将选择新的 boolean/setting 并采取适当的行动。

有关计时的详细信息,请参阅 notes section on MDN

JavaScript 是单线程的 所以即使你有一个多处理器设备 javascript 代码也会在那个单线程上执行。

setInterval 运行s "asynchronously",但是... 异步并不意味着代码 运行s 在单独的线程或处理器。这意味着您的同步代码和异步代码是在队列中发布的时间片。并且只有一个线程按顺序为该队列中的所有切片提供服务。

您的 setInterval 将尝试每 # 毫秒执行一次,但它会被其他操作阻塞,并且只会在唯一线程可用时执行.所以 setInterval 不能保证在指定的时间间隔执行。

当键(或鼠标)事件被触发时,该事件被放入队列,但不调用键事件处理程序。该关键事件处理程序将 运行 当轮到其原始事件出现在队列中时。