【问题标题】:How can I delay code execution with setTimeout?如何使用 setTimeout 延迟代码执行?
【发布时间】:2021-09-19 22:37:58
【问题描述】:

我仍然是一个 JS 初学者,试图掌握 jQuery 并开始将 Simon Game 编码为一个项目。您有 4 个彩色空间,它们应该以随机序列点亮并播放声音,从长度为 1 的序列开始。玩家通过单击方块重新创建该序列。如果成功,随机序列长度将增加1,如果你犯了错误,你必须重新开始。

我正处于简单地创建长度为 5 的随机序列并直观地显示该序列的阶段。这就是我想出的:

var sequence = [];

$("button").click(startGame);

function startGame() {
  for (let index = 0; index < 5; index++) {
    sequence.push(nextColour());
    touchField(index);
    // playSound();
  }
}

function nextColour() {
  var randomNumber = Math.floor(Math.random() * 4) + 1;
  if (randomNumber === 1) {
    var nextColour = "red";
  } else if (randomNumber === 2) {
    var nextColour = "blue";
  } else if (randomNumber === 3) {
    var nextColour = "green";
  } else {
    var nextColour = "yellow";
  }
  return nextColour
};

function touchField(index) {
  $("." + sequence[index]).addClass("active");
  setTimeout(function() {
    $("." + sequence[index]).removeClass("active")
  }, 300);
}
.active {
  border: 1rem solid purple;
}

.red {
  border: 1px solid red;
}

.blue {
  border: 1px solid blue;
}

.green {
  border: 1px solid green;
}

.yellow {
  border: 1px solid yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<img class="red" src="https://via.placeholder.com/50" alt="">
<img class="blue" src="https://via.placeholder.com/50" alt="">
<img class="green" src="https://via.placeholder.com/50" alt="">
<img class="yellow" src="https://via.placeholder.com/50" alt="">

所以问题是,“.active”类几乎同时从所有方格中添加和删除 - 这使得无法区分任何类型的序列。所以我想我可以使用“setTimeout();”。但这并不妨碍它这样做。我相信发生的事情是,直到确实执行超时后要评估的表达式,代码才会继续运行,因此几乎同时添加“.active”和超时 5 次 - 这导致它们几乎同时删除。理想情况下,在删除“.active”和将“.active”添加到下一个方块之间有足够的时间。我还尝试将 setTimeout 添加到“touchSquare()”函数调用中。它也不起作用 - 我相信出于同样的原因,它只是继续执行。我希望你能看到我的问题。 ^^

我看过其他问题,但答案似乎忽略了浏览器在到达 setTimeout 后继续执行代码的事实,例如这里:

How to delay execution in between the following in my javascript

如果您将其复制粘贴到控制台中,它根本不会做它应该做的事情,因为在识别出 setTimeout 之后代码会继续执行。我希望我能让我的问题对你有意义,这是我第一次在 StackOverflow 上发布问题。如果我能以任何方式改进我提出问题的方式,我非常感谢建设性的批评!

【问题讨论】:

  • setTimeout 用于在指定延迟后运行回调。它确实阻止执行该语句之后的代码。请参阅Window.setTimeout() 的文档。 This tutorial 解释了阻塞与非阻塞代码,这在您的案例中是混淆的根源。
  • 欢迎。您经常会遇到的一个主题是 jQuery 不再是必需品,它实际上会成为您学习和现代应用程序开发的障碍。浏览器已经标准化,JavaScript 几乎可以完成 jQuery 开箱即用的所有功能。先学 JS。
  • 这能回答你的问题吗? What is the JavaScript version of sleep()?
  • 为什么只暂停 300 毫秒。 3000 不是更有意义吗?
  • @zipzit,我不确定这有什么关系。此外,三秒在 UI 中是永恒的,更不用说一堆三秒的间隔了。

标签: javascript html jquery


【解决方案1】:

一种方法是将setTimeout 用作异步循环,即回调将安排一个新的计时器并重复setTimeout 调用。但是,由于您希望在完成后继续处理其他内容,因此您很快就会进入所谓的“回调地狱”。

我建议您合并承诺。 JavaScript 具有 asyncawait 语法,可以通过 Promise 简化编程。

所以首先要做的是创建setTimeout 的等价物,但是作为一个返回promise 的函数,你可以await

那么剩下的代码几乎不需要改动。

不过我建议:

  • 将索引存储在sequence 数组中,而不是颜色名称。
  • 将图像的引用放在一个 jQuery 集合的开头。
  • 在动画期间隐藏开始按钮 - 您不希望用户干扰该动画。
  • 确保在每次调用 startGame 时重置 sequence 数组。

这是它的工作原理。忽略我添加的其他 CSS,因为我无权访问您的图像。

const sequence = [];

// Load the elements in an node list
const $images = $(".red, .blue, .green, .yellow");

$("button").click(startGame);

// Helper function
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));

async function startGame() {
  $("button").hide(); // Don't allow a click on Start while the sequence is animating
  sequence.length = 0; // reset
  for (let index = 0; index < 5; index++) {
    sequence.push(nextColour());
    await touchField(sequence[index]);
  }
  $("button").show();
  console.log(sequence);
  // Continue with other logic here...
}

function nextColour() {
  return Math.floor(Math.random() * 4);
};

async function touchField(index) {
  $images.eq(index).addClass("active");
  await delay(300);
  $images.eq(index).removeClass("active");
  await delay(100);
}
.active {
  border: 1rem solid purple;
}

.red    { background-color: red;    }
.blue   { background-color: blue;   }
.green  { background-color: green;  }
.yellow { background-color: yellow; }

img {
    display: inline-block;
    width: 70px;
    height: 50px;
    border: 1rem solid white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<img class="red" src="/projects/simon-game/images/red.png" alt="">
<img class="blue" src="/projects/simon-game/images/blue.png" alt="">
<img class="green" src="/projects/simon-game/images/green.png" alt="">
<img class="yellow" src="/projects/simon-game/images/yellow.png" alt="">
<p>
<button>Start</button>

【讨论】:

    【解决方案2】:

    要了解它似乎忽略了setTimeout() 并且事情似乎同时发生的原因,您需要了解stackevent loop 之间的区别。

    Concurrency model and the event loop

    放入堆栈的事情会立即(或尽快)发生,这就是 for 循环正在做的事情。

    放入事件循环的事情在给定的最短时间后发生(之后,不完全是),这就是 setTimeout 正在做的事情。

    换句话说,for 循环将 setTimouts 放入事件循环中的时间间隔为毫秒,因此超时时间间隔为毫秒。事件循环不像这样做,then this,then that。它的工作方式类似于“此事件是否达到或超过了它的超时时间?如果是,请尽快执行”

    要达到你想要的效果,你需要一些recursion,或者当一个函数调用自己时。这样下一个TouchField() 在前一个完成之前不会被调用,从而按照您期望的方式将它们分开。

    我做了一些非初学者的事情来简化随机序列的生成(使用Array.from() 和一个值数组而不是冗长的 if/else 块),并且我添加了第二个超时以便有一个显示字段之间的延迟(否则同一字段连续两次模糊在一起),但希望这有助于您理解这个概念。

    let level = 5, sequence; //increment the level /sequence length as the player progresses
    
    $('button').click(function startGame() {
        sequence = Array.from({ length: level },
          field => ['red','blue','green','yellow'][Math.floor(Math.random() * 4)]);
        
        TouchField(); //no argument to start recursion / show sequence
    });
    
    function TouchField(index = 0) {
      if (index <= sequence.length - 1) { //recursion exit condition
        // playSound();
        $('.' + sequence[index]).addClass('active');
    
        setTimeout(function() {
          $('img').removeClass('active');
          
          setTimeout(function() {
            TouchField(index + 1); //recursion
          }, 500);
        }, 500);
      }
    }
    .active {
      border: 1rem solid purple !important;
    }
    
    .red {
      border: 1px solid red;
    }
    
    .blue {
      border: 1px solid blue;
    }
    
    .green {
      border: 1px solid green;
    }
    
    .yellow {
      border: 1px solid yellow;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    
    <img class="red" src="https://via.placeholder.com/50" alt="">
    <img class="blue" src="https://via.placeholder.com/50" alt="">
    <img class="green" src="https://via.placeholder.com/50" alt="">
    <img class="yellow" src="https://via.placeholder.com/50" alt="">
    <div>
      <button>PLAY</button>
    </div>

    【讨论】:

      猜你喜欢
      • 2012-10-30
      • 2014-09-25
      • 2020-07-22
      • 1970-01-01
      • 2016-12-11
      • 1970-01-01
      • 1970-01-01
      • 2013-10-02
      • 2011-10-18
      相关资源
      最近更新 更多