Javascript SetTimeout的执行速度比预期的快

Javascript SetTimeout的执行速度比预期的快,javascript,html,css,animation,timer,Javascript,Html,Css,Animation,Timer,我正在学习javascript: 我目前正在尝试做一个游戏,在这个游戏中,动物每次随机产卵一到两秒,然后我们可以点击它们,让它们消失 我面临一个困难,因为我添加了一个SetTimeout来等待跳跃动画结束,以删除它的CSS类来删除动物 然而,动物只会第一次出现,然后,跳转功能的执行很快,所以动物不会出现 我试着调试程序,首先我们看到动物在同一个div上,这个div是随机计算的: 在下一步中,我们看到动物出现在第二个孔上,调试器仍然记录使用的孔是第五个: 在下一步中,我们将看到使用了div 2

我正在学习javascript:

我目前正在尝试做一个游戏,在这个游戏中,动物每次随机产卵一到两秒,然后我们可以点击它们,让它们消失

我面临一个困难,因为我添加了一个SetTimeout来等待跳跃动画结束,以删除它的CSS类来删除动物

然而,动物只会第一次出现,然后,跳转功能的执行很快,所以动物不会出现

我试着调试程序,首先我们看到动物在同一个div上,这个div是随机计算的:

在下一步中,我们看到动物出现在第二个孔上,调试器仍然记录使用的孔是第五个:

在下一步中,我们将看到使用了div 2:

调试器仍然说,当动物从1中产卵时,使用的是div 2:

此外,如果我尝试使用调试器执行程序,动物只会第一次显示,然后总是隐藏。另外,在控制台中,我们可以看到很多console.log,每秒都有时间和漏洞

以下是当前代码:

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Whack A Mole!</title>
    <link href='https://fonts.googleapis.com/css?family=Amatic+SC:400,700' rel='stylesheet' type='text/css'>
    <link rel="stylesheet" href="style.css">
</head>
<body>

<h1>Whack-a-mole! <span class="score">0</span></h1>
<button onClick="startGame()">Start!</button>

<div class="game">
    <div class="hole hole1">
        <div class="mole"></div>
    </div>
    <div class="hole hole2">
        <div class="mole"></div>
    </div>
    <div class="hole hole3">
        <div class="mole"></div>
    </div>
    <div class="hole hole4">
        <div class="mole"></div>
    </div>
    <div class="hole hole5">
        <div class="mole"></div>
    </div>
    <div class="hole hole6">
        <div class="mole"></div>
    </div>
</div>

<script>
    const holes = document.querySelectorAll('.hole');
    const scoreBoard = document.querySelector('.score');
    const moles = document.querySelectorAll('.mole');

    let lastHole;

    function randomTime(min, max) {
        const time = Math.round(Math.random() * (max - min) + min);
        console.log(time);
    }

    function randomHole(holes) {
        const index = Math.floor(Math.random() * holes.length);
        const hole = holes[index];
        if (hole === lastHole) {
            console.log('We calculate a new hole!');
            return randomHole(holes);
        }
        lastHole = hole;
        return hole;
    }

    function jump() {
        let time = randomTime(1000, 2000);
        let hole = randomHole(holes);
        console.log(time, hole);
        hole.classList.add('up');
        setTimeout(() => {
            hole.classList.remove('up');
            debugger;
            jump();
        }, time);
    }

    jump();

</script>
</body>
</html>
编辑:


谢谢斯科特·马库斯的帮助,我很感激

我已经尝试了以下代码,我将只使用跳转函数:

<script>
    const holes = document.querySelectorAll('.hole');
    const scoreBoard = document.querySelector('.score');
    const moles = document.querySelectorAll('.mole');

    let lastHole;
    let timer = null;

    function randomTime(min, max) {
        const time = Math.round(Math.random() * (max - min) + min);
        console.log(time);
    }

    function randomHole(holes) {
        const index = Math.floor(Math.random() * holes.length);
        const hole = holes[index];
        if (hole === lastHole) {
            console.log('We calculate a new hole!');
            return randomHole(holes);
        }
        lastHole = hole;
        return hole;
    }

    function jump() {
        clearTimeout(timer);
        let time = randomTime(1000, 2000);
        let hole = randomHole(holes);
        console.log(time, hole);
        hole.classList.add('up');
        timer = setTimeout(() => {
            hole.classList.remove('up');
            debugger;
            jump();
        }, time);
    }

    jump();

</script>

const holes=document.queryselectoral('.hole');
const scoreBoard=document.querySelector('.score');
常量moles=document.queryselectoral('.mole');
让最后一个洞;
设定时器为空;
函数随机时间(最小值、最大值){
常量时间=Math.round(Math.random()*(max-min)+min);
console.log(时间);
}
功能随机孔(孔){
const index=Math.floor(Math.random()*holes.length);
常数孔=孔[索引];
如果(孔===最后一个孔){
log('我们计算一个新孔!');
返回孔(孔);
}
最后一个孔=孔;
回流孔;
}
函数跳转(){
清除超时(计时器);
让时间=随机时间(10002000);
让孔=随机孔(孔);
控制台日志(时间、孔);
hole.classList.add('up');
计时器=设置超时(()=>{
hole.classList.remove('up');
调试器;
跳跃();
},时间);
}
跳跃();
我发现,在Opera中,如果我们用鼠标滚轮滚动,动物就会产卵:

但是,我们还没有在脚本上添加evenetListener

此外,在Mozilla中,它们根本不会出现:

即使我们在控制台中看到如何计算和显示randomTime和randomHole

我们怎样才能让动物产卵

我认为它们没有显示的原因是计时器执行得太快,即使我们保存最后一个,最后一个计时器并在它尚未完成时清除它


谢谢你的帮助

由于
跳转
函数包含
设置超时
,并且计时器的回调函数对
跳转
进行递归调用,因此在第一个计时器运行或完成之前,可能会发生对
跳转
的第二次调用。这可能会导致多个计时器回调函数在事件队列中“堆叠”起来并一个接一个地运行。这很可能是您看到的行为

您需要确保对
跳转的额外调用不会启动额外的计时器,这可能会导致您描述的效果。

您可以通过确保一次只能运行一个计时器来实现这一点,这是通过捕获计时器的唯一标识符并确保在启动新计时器之前取消最后一个计时器来实现的

var timer = null; // This will hold the most recent timer's id

function jump() {
    clearTimeout(timer); // Cancel any previous timer

    let time = randomTime(1000, 2000);
    let hole = randomHole(holes);
    console.log(time, hole);
    hole.classList.add('up');

    // Capture a reference to the most recent timer
    timer = setTimeout(() => {
        hole.classList.remove('up');
        debugger;
        jump();
    }, time);
}

感谢斯科特·马库斯的帮助。我尝试将当前计时器存储在一个变量中,然后将其作为第一个跳转的指令发出,但它们在Mozilla中根本不显示,在Opera中,它们只在第一次执行时显示,然后在第一个计时器消失后,其他计时器就不会显示。@Enoy老实说,我还没有查看您的所有代码。您很可能在代码的其他方面仍然存在问题,但我在这里分享的内容解决了您所问的计时器的加速问题。
var timer = null; // This will hold the most recent timer's id

function jump() {
    clearTimeout(timer); // Cancel any previous timer

    let time = randomTime(1000, 2000);
    let hole = randomHole(holes);
    console.log(time, hole);
    hole.classList.add('up');

    // Capture a reference to the most recent timer
    timer = setTimeout(() => {
        hole.classList.remove('up');
        debugger;
        jump();
    }, time);
}