繁体   English   中英

如何使用 setTimeout 延迟代码执行?

[英]How can I delay code execution with setTimeout?

我仍然是一个 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 后继续执行代码的事实,例如:

如何在我的javascript中延迟以下之间的执行

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

一种方法是使用setTimeout作为异步循环,即回调将使用重复的setTimeout调用安排新的计时器。 但是当你完成之后想要继续处理其他事情时,你很快就会进入所谓的“回调地狱”。

我建议你加入承诺。 JavaScript 具有asyncawait语法,可以通过 promise 简化编程。

所以首先要做的是创建setTimeout的等价物,但作为一个返回承诺的函数,你可以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>

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

并发模型和事件循环

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

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

换句话说,for 循环在彼此相隔几毫秒的时间内将 setTimouts 放入事件循环中,因此超时在彼此相隔几毫秒内触发。 事件循环不像这样,然后这个,然后那个那样工作。 它的工作原理类似于“此事件是否已达到或超过它的超时时间?如果是,请尽快执行”

为了达到您正在寻找的效果,您需要一些递归,或者在函数调用自身时。 这样,直到前一个TouchField()才会调用下一个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>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM