简体   繁体   English

使用setTimeout强制创建新元素之间的延迟

[英]Using setTimeout to force delays between creating new elements

I'm trying to make ap then have a delay, then make another p my battle function works until I try to add setTimeout. 我试图使ap再有一个延迟,然后再使另一个p生效,直到我尝试添加setTimeout为止。 I've tried using setTimeout just about everywhere to no effect. 我尝试在几乎所有地方都使用setTimeout无效。 Sometimes it creates an infinite loop, and in some places it can make a delay before it creates every p at the same time. 有时,它会创建一个无限循环,并且在某些情况下,它可能会延迟一段时间才能同时创建每个p。

The effect should be similar to this Click Attack Button 效果应类似于此“ 单击攻击”按钮

I tried using the same method as this codepen's creator but I don't understand how his code works. 我尝试使用与此Codepen的创建者相同的方法,但我不了解他的代码如何工作。

Here is the Demo 这是演示

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();
});

i tried explaining in a comment, but it won't fit because there's a couple things going on here. 我尝试在评论中进行解释,但由于其中发生了几件事,因此不适合。 it's easier just to fix it and talk about the new code: 修复它并讨论新代码会更容易:

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));
  }
}

here i've moved the computations to the top of the loop, then defered the visible action of the computations behind a setTimeout for 1/2 second. 在这里,我将计算移到了循环的顶部,然后将计算的可见操作推迟到setTimeout之后1/2秒。

you needed private copies of the vars altered by the loop, and you needed a timeout that could use those values. 您需要由循环更改的var的私有副本,并且您需要一个可以使用这些值的超时。

You could alter your battle function a bit and instead of using a while-loop, simply let the setTimeout() call loop it for you. 您可以稍微改变战斗功能,而不是使用while循环,只需让setTimeout()调用为您循环即可。

The following is an example of my modifications to your linked fiddle: 以下是我对链接的小提琴的修改示例:

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();
});

You can see my JSFiddle here 你可以在这里看到我的JSFiddle

The problem you are having is with your implementation of timeout . 您遇到的问题与timeout的实现有关。 As tymeJV mentioned in the comments, setTimeout is async. 正如评论中提到的tymeJV一样, setTimeout是异步的。 Your code will not stop what it is doing to complete the timeout. 您的代码不会停止正在完成超时的操作。 Instead you have to pass a callback to be completed upon the time being elapsed (in your case appending the element). 取而代之的是,您必须传递一个回调,以在经过一段时间后完成(在您的情况下,添加元素)。

Try this code snippet in your console to better illustrate this: 在您的控制台中尝试以下代码片段,以更好地说明这一点:

setTimeout(function() {
  console.log('hello from callback');
}, 3000)

console.log('hello from outside');

As you can see the later code gets run first even though it is after the timeout. 如您所见,即使是在超时之后,后面的代码也会首先运行。

The accepted solution starts a number of setTimeout() calls in parallel. 接受的解决方案并行启动许多setTimeout()调用。 In your case it will tend to be less than a dozen concurrent timers - although there's no guarantee how many timers it will start because of the random numbers. 在您的情况下,它通常少于十二个并发计时器-尽管由于随机数不能保证它将启动多少个计时器。

In most cases it's better to have each timeout start the next one, instead of firing them up all at once like this. 在大多数情况下,最好让每个超时都开始下一个超时,而不是像这样一次触发所有超时。 For example, imagine that you need to report periodic progress on a much longer running process than this, or a process that keeps running until the program exits? 例如,假设您需要报告一个比该过程更长的运行过程的定期进度,或者一个持续运行直到程序退出的过程? You would need a very large number of concurrent timeouts. 您将需要大量的并发超时。

Here's a version of your code with minimal changes from your original and uses only a single setTimeout() at any moment, to avoid this problem. 这是您的代码版本,与原始代码相比变化很小,并且随时仅使用一个setTimeout()来避免此问题。

The way it works is simple: it doesn't use a loop at all, and instead battle() calls timeout() if it needs another run. 它的工作方式很简单:它根本不使用循环,而如果需要再次运行, battle()调用timeout() timeout() in turn passes battle as the parameter to setTimeout() , so battle() will be called when the time elapses. timeout()依次传递battle作为参数setTimeout()所以battle()将被称为当时间过去。

I marked the changes with //** for you: 我用//**为您标记了更改:

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();
});

Working fiddle 工作提琴

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

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