我试图制作一个p,然后有一个延迟,然后制作另一个p。我的战斗函数可以工作,直到我尝试添加setTimeout。我试过在任何地方都使用setTimeout,但没有效果。有时它会创建一个无限循环,在某些地方,它可以在同时创建每个p之前进行延迟。
效果应该类似于点击攻击按钮
我试着使用与这个代码笔的创建者相同的方法,但我不明白他的代码是如何工作的。
这是演示
function timeout() {
setTimeout(function() {
}, 500);
}
var battle = function() {
while (monsterHP > 0) {
var playerDam = Math.floor(Math.random() * ((playerAtk - monsterAtk) + 2));
var newP = $('#battle').append("<p>You have hit the monster for " + playerDam + " damage. The monster has " + (monsterHP - playerDam) + "HP left</p>");
monsterHP -= playerDam;
timeout();
if (monsterHP <= 0) {
$('#battle').append("<p>You have defeated the monster</p>");
}
}
}
$('#battleButton').click(function() {
battle();
});
我试着在评论中解释,但它不适合,因为这里发生了一些事情。修复它并谈论新代码更容易:
var battle = function() {
var i=0;
while (monsterHP > 0) {
(function(hp){
var playerDam = Math.floor(Math.random() * ((playerAtk - monsterAtk) + 2));
monsterHP-= playerDam;
hp= monsterHP; // this might or might not be needed.
setTimeout(function(){
var newP = $('#battle').append("<p>You have hit the monster for " + playerDam + " damage. The monster has " + (hp - playerDam) + "HP left</p>");
if (hp<= 0)$('#battle').append("<p>You have defeated the monster</p>");
}, i++ * 500);
}(monsterHP));
}
}
在这里,我已经将计算移动到循环的顶部,然后将计算的可见操作推迟到setTimeout之后1/2秒。
您需要由循环更改的vars的私有副本,并且需要一个可以使用这些值的超时。
您可以稍微更改您的battle函数,而不是使用while循环,只需让setTimeout()
调用为您循环即可。
以下是我修改你的链接小提琴的例子:
var playerAtk = 5;
var playerDef = 5;
var playerHP = 10;
var monsterAtk = 4;
var monsterDef = 4;
var monsterHP = 8;
var battle = function() {
if (monsterHP > 0)
{
var playerDam = Math.floor(Math.random() * ((playerAtk - monsterAtk) + 2));
var newP = $('#battle').append("<p>You have hit the monster for " + playerDam + " damage. The monster has " + (monsterHP - playerDam) + "HP left</p>");
monsterHP -= playerDam;
setTimeout(function() {
battle();
}, 1000);
}
else {
$('#battle').append("<p>You have defeated the monster</p>");
}
}
$('#battleButton').click(function() {
battle();
});
你可以在这里看到我的JSFiddle
您遇到的问题是timeout
的实现。正如tymeJV在评论中提到的,setTimeout
是异步的。您的代码不会停止为完成超时而执行的操作。相反,您必须传递一个回调,以便在经过时间后完成(在您的情况下是附加元素)。
请在控制台中尝试此代码片段,以更好地说明这一点:
setTimeout(function() {
console.log('hello from callback');
}, 3000)
console.log('hello from outside');
正如您所看到的,即使在超时之后,后面的代码也会首先运行。
接受的解决方案并行启动多个setTimeout()
调用。在你的情况下,它往往只有不到12个并发定时器——尽管由于随机数的原因,无法保证它会启动多少个定时器。
在大多数情况下,最好每次暂停都从下一次开始,而不是像这样一次启动。例如,假设您需要报告一个运行时间比这长得多的进程的定期进度,或者一个一直运行到程序退出的进程?您将需要大量的并发超时。
这是一个代码版本,与原来的代码相比变化最小,并且在任何时候都只使用一个setTimeout()
,以避免这个问题。
它的工作方式很简单:它根本不使用循环,而是battle()
在需要再次运行时调用timeout()
。timeout()
依次将battle
作为参数传递给setTimeout()
,因此当时间流逝时将调用battle()
。
我用//**
为您标记了更改:
var playerAtk = 5;
var playerDef = 5;
var playerHP = 10;
var monsterAtk = 4;
var monsterDef = 4;
var monsterHP = 8;
function timeout() {
//** Your original setTimeout call didn't do anything at all.
//** setTimeout isn't a "delay and then return when the delay is done".
//** Instead, it returns immediately, and the function you pass into it
//** will be run after the time elapses.
//** Here we pass battle, so that function is called after the timeout.
setTimeout(battle, 500);
}
var battle = function() {
//** This is now just an if instead of a while. It doesn't loop at all here.
//** Instead, when we want to run another iteration, the setTimeout() call
//** takes care of that by calling battle again.
//** In fact, if monsterHP always starts positive, you don't need this
//** if statement at all.
if (monsterHP > 0) {
var playerDam = Math.floor(Math.random() * ((playerAtk - monsterAtk) + 2));
var newP = $('#battle').append("<p>You have hit the monster for " + playerDam + " damage. The monster has " + (monsterHP - playerDam) + "HP left</p>");
monsterHP -= playerDam;
if (monsterHP <= 0) {
$('#battle').append("<p>You have defeated the monster</p>");
} else {
//** The final change: if monsterHP is still > 0, we call timeout()
//** here to start the next battle call after the interval elapses.
timeout();
}
}
}
$('#battleButton').click(function() {
battle();
});
工作小提琴