如何在Javascript中延迟setInterval?

5

我最近在JavaScript中遇到了一个奇怪的问题,就是似乎不能把setInterval的延迟时间设置得更长。

下面是一个小例子:

var loop;
var count;
loop = setInterval(start, 30);

function start() {
    //Some code

    delay();

    //Some more code
}

function delay() {
    setTimeout(function () {
        count++;

        //Animation code

        if (count <= 1000) delay();
    }, 100);
}

现在我想要做的是,在执行“一些代码”部分时,不执行任何操作,直到“动画代码”执行完毕,然后执行“更多代码”部分。
问题在于,“更多代码”部分和延迟函数同时被执行。我尝试在调用延迟函数之前使用clearInterval(loop),并在延迟函数完成执行后重新启动它,但“更多代码”似乎仍然会被执行一次。
如何解决这个问题?请给出详细的解释和示例。

2
你究竟想要实现什么目标? - Stefano Mtangoo
请查看此链接- http://jsfiddle.net/fveka675/ - Manoz
6个回答

6

您的delay()函数并不会真正阻止start()函数的完成。

在Javascript中,setTimeout()并不会暂停Javascript的执行。相反,它会安排传递给setTimeout()的回调在未来的某个时间运行,而您的其余代码仍将继续运行。

如果您想要延迟某段代码块的执行一段时间,则必须将该代码块放置在setTimeout()回调中,如下所示:

setTimeout(function() {
    // the code in here will run some time in the future
}, delayTime);

// the code here will run immediately

通常在JavaScript中描述setTimeout()的方式是,称其为非阻塞的。它不会停止或暂停当前执行线程的运行。实际上,该执行线程将一直运行到完成。相反,setTimeout()会在未来的某个特定时间(当没有其他JavaScript代码在运行时)安排一个回调函数。
由于JavaScript是单线程的,即使您使用一个非常长的while()循环,循环创建延迟也几乎从不起作用。问题在于,当您正在循环时,没有其他JavaScript代码可以运行,因此没有其他状态可以更改,因此您等待更改的任何循环条件在循环过程中都不会发生变化。这通常会导致死锁类型的情况,最终浏览器会意识到JavaScript线程“卡住”并提示终止它。
相反,您需要使用事件和未来的回调函数进行编程。您设置一个事件或计时器回调,以便在某些未来的时间点调用它,然后将相关代码放在该回调或事件处理程序中。还有更高级的工具可帮助管理此操作,例如promises或async库,但它们只是建立在相同的回调机制之上。

setTimeout(setInterval(_=>{功能}, 延迟时间), 延迟时间) 对我不起作用。有没有方法让它工作? - leto
@mistarsv - 你必须传递一个函数给setTimeout()。你正在立即执行setInterval()并将setInterval()返回的timerID传递给setTimeout()。将你的setInterval()包装在另一个箭头函数中,这样你就可以将该箭头函数传递给setTimeout() - jfriend00

1

如果没有回调函数,JavaScript 中无法真正执行延迟操作。

var loop;
var count;
loop=setInterval(start,30);
function start()
{
    //Some code
    setTimeout(function(){
        //Some more code
    }, 10000);

}

理想情况下,您的动画方法应该有一个回调函数,例如“completed”或类似的名称。

1
很高兴你已经解决了问题。 :-)
我想发表一下我的看法,因为我非常喜欢使用单个requestAnimationFrame循环来创建动画。
1. 创建一个JavaScript对象数组,表示时间线中的每个单独步骤。 2. 在单个requestAnimationFrame循环中按顺序“播放”每个步骤。
这里有一个带注释的示例和演示:

// canvas related variables
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;

// define step1 in the timeline
var step1={
  startX:0,
  endX:100,
  currentX:0,
  onDraw:function(){
    ctx.fillStyle='blue';
    ctx.fillRect(this.currentX,50,75,60);
  },
  onAnimate:function(){
    this.currentX++;  // or however you want myRect1 moved this frame
    if(this.currentX>=this.endX){
      this.currentX=this.endX;
      this.isCompleted=true;
    }
  },
  isCompleted:false
};

// define step2 in the timeline
var step2={
  startX:200,
  endX:100,
  currentX:200,
  onDraw:function(){
    ctx.fillStyle='gold';
    ctx.fillRect(this.currentX,50,75,60);
  },
  onAnimate:function(){
    this.currentX--;  // or however you want myRect1 moved this frame
    if(this.currentX<=this.endX){
      this.currentX=this.endX;
      this.isCompleted=true;
    }
  },
  isCompleted:false
}

// put all steps in an array
var steps=[ step1, step2 ];

// nextTime is used to trigger re-rendering
var nextTime=0;
// each animated frame will occur every 50 ms
var delayPerFrame=1000/60*3;

// start the animation loop
requestAnimationFrame(animate);

// the animation loop
function animate(time){

  // return if 50ms has not elapsed
  if(time<nextTime){requestAnimationFrame(animate); return;}

  // set the next elapsed time
  nextTime=time+delayPerFrame;

  // clear the canvas
  ctx.clearRect(0,0,cw,ch);

  // redraw all steps and also
  // find the active step in steps[] & run it's onAnimate
  var isAnimating=false;
  for(var i=0;i<steps.length;i++){
    var s=steps[i];
    if(!isAnimating && !s.isCompleted){
      s.onAnimate();
      isAnimating=true;
    }
    s.onDraw();
  }

  // request another loop if all steps are not yet completed
  if(isAnimating){
    requestAnimationFrame(animate);
  }else{
    alert('The animation is complete');
  }

}
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<h4>Step1: Blue rect moves rightward<br>Step2: Gold rect move leftward.</h4>
<canvas id="canvas" width=300 height=300></canvas>


1
谢谢!这对我很有用! - Aswin G

0

你甚至可以消除超时。

这可以防止在上一个函数完成之前启动你的start()函数。

function start() {
    animate(function(){
        someMoreCode();
    });    
}

function animate(callback) {    
    //animation code

    //once animation is done call a callback
    callback();
}
function someMoreCode(){
    //Some more code

    //once everything's done then start again
    start();
}

start();

0
该代码在首次执行前等待initialDelay秒,然后每隔delay秒执行一次。
const initialDelay = 3
const delay = 5
let interval

setTimeout(() => {
  // call your func here
  interval = setInterval(() => {
    // call your func here
  }, delay * 1000)
}, initialDelay * 1000)

在某些时候或销毁时,您可能需要使用clearInterval(interval)来停止重复执行。


0

我发现将函数设置并在其中执行,然后将延迟操作传递给外部的setTimeout()更加清晰,如下所示:

function delayedFn() {
   //whichever to do here.. 
}
  
 
    //set the timing.. 
    setTimeout(delayedFn(){
    }, 10000);


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接