SetTimeout выполняется быстрее, чем ожидалось

Я изучаю javascript:

В настоящее время я пытаюсь сделать игру, в которой животные должны появляться каждый случайный момент времени между одной и двумя секундами, а затем мы могли бы щелкнуть по ним и заставить их исчезнуть.

Я столкнулся с трудностью, потому что я добавил SetTimeout, чтобы дождаться окончания анимации прыжка, чтобы удалить его класс CSS для удаления животного.

Однако животные появляются только в первый раз, а затем выполнение функции прыжка происходит быстро, поэтому животные не появляются.

Я попытался отладить программу, сначала мы видим животное в том же элементе div, который был рассчитан случайным образом:

введите здесь описание изображения

На следующем шаге мы видим, что животное появляется на второй дырке, а отладчик все равно записывает, что использована пятая дырка:

введите здесь описание изображения

На следующем шаге мы видим, что используется div 2:

введите здесь описание изображения

И снова отладчик по-прежнему говорит, что это div 2 использовался, когда животное появлялось из 1:

введите здесь описание изображения

Кроме того, если я пытаюсь выполнить программу с помощью отладчика, животные отображаются только в первый раз, а затем всегда прячутся. Плюс в консоли мы видим много 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>

Я предполагаю, что это функция jump(), которая ведет себя неожиданно, однако, как мы могли создать животное, дождаться перехода и скрыть его, а затем заставить появиться новое?.

стиль.css

html {
  box-sizing: border-box;
  font-size: 10px;
  background: #ffc600;
}

*, *:before, *:after {
  box-sizing: inherit;
}

body {
  padding: 0;
  margin:0;
  font-family: 'Amatic SC', cursive;
}

h1 {
  text-align: center;
  font-size: 10rem;
  line-height:1;
  margin-bottom: 0;
}

.score {
  background:rgba(255,255,255,0.2);
  padding:0 3rem;
  line-height:1;
  border-radius:1rem;
}

.game {
  width:600px;
  height:400px;
  display:flex;
  flex-wrap:wrap;
  margin:0 auto;
}

.hole {
  flex: 1 0 33.33%;
  overflow: hidden;
  position: relative;
}

.hole:after {
  display: block;
  background: url(dirt.svg) bottom center no-repeat;
  background-size:contain;
  content:'';
  width: 100%;
  height:70px;
  position: absolute;
  z-index: 2;
  bottom:-30px;
}

.mole {
  background:url('mole.svg') bottom center no-repeat;
  background-size:60%;
  position: absolute;
  top: 100%;
  width: 100%;
  height: 100%;
  transition:all 0.4s;
}

.hole.up .mole {
  top:0;
}

РЕДАКТИРОВАТЬ:

Спасибо @Scott Marcus за вашу помощь, я ценю это.

Я пробовал следующий код, я просто поставлю функцию перехода:

<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>

Я обнаружил, что если мы прокручиваем колесо мыши, животные появляются в Opera:

введите здесь описание изображения

Однако мы не добавили в сценарий eventListener.

Кроме того, в Mozilla они вообще не отображаются:

введите здесь описание изображения

Даже если мы увидим в консоли, как вычисляются и отображаются randomTime и randomHole.

Как мы могли заставить животных нереститься?

Я думаю, что причина, по которой они не отображаются, заключается в том, что таймеры работают слишком быстро, даже если мы сохраним последний, последний таймер и очистим его, если он еще не закончился.

Спасибо за помощь!!


person Enoy    schedule 08.04.2018    source источник


Ответы (1)


Поскольку ваша функция jump содержит setTimeout, а функция обратного вызова таймера делает рекурсивный вызов jump, у вас есть сценарий, в котором второй вызов jump может произойти до того, как первый таймер запустится или завершится. Это может привести к тому, что несколько функций обратного вызова таймера «накопятся» в очереди событий и будут запускаться одна сразу за другой. Вероятно, это поведение, которое вы видите.

Необходимо убедиться, что дополнительные вызовы jump не инициируют дополнительные таймеры, которые могут привести к описанному вами эффекту.

Вы достигаете этого, гарантируя, что только один таймер может работать одновременно, и это достигается путем захвата уникального идентификатора таймера и обязательной отмены последнего таймера перед запуском нового.

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);
}
person Scott Marcus    schedule 08.04.2018
comment
Спасибо @Scott Marcus за вашу помощь. Я попытался сохранить текущий таймер в переменной, а затем создать crearTimeout как инструкцию первого перехода, однако они вообще не отображаются в Мозилле, а в Опере они отображаются только при первом выполнении, затем после исчезновения первого другие не отображаются вверх. - person Enoy; 08.04.2018
comment
@Enoy Честно говоря, я не просматривал весь твой код. У вас, скорее всего, все еще есть проблемы с другими аспектами кода, но то, что я здесь поделился, касается ускорения таймеров, о котором вы спрашивали. - person Scott Marcus; 08.04.2018