How to delay setInterval in Javascript?
Asked Answered
L

6

5

I've been running into a weird problem repeatedly in JavaScript now. I can't seem to delay setInterval longer.

A small example of what happens:

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

Now what I want to do is, execute 'some code' part, do nothing while the 'animation code' executes, and then execute 'some more code' part.

What is happening is that the 'some more code' part and the delay function is getting executed simultaneously. I have tried clearInterval(loop) before calling the delay function and restarting it after the delay function completes it's execution, but the 'some more code' seems to get executed once anyways.

How can I get around this problem? Please give a detailed explanation with an example.

Luxuriant answered 5/4, 2015 at 8:37 Comment(2)
what exactly are you trying to accomplish?Toiletry
have a look at this- jsfiddle.net/fveka675Heavily
B
6

Your delay() function does not actually stop your start() function from completing.

In Javascript, setTimeout() does not pause the execution of Javascript. Instead, what it does is schedule the callback passed to setTimeout() to run sometime in the future and the rest of your code just keeps on running.

If you want to delay the execution of some block of code for some time period, then you have to put that block of code inside the setTimeout() callback like this:

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

// the code here will run immediately

The way this is often described in Javascript is to say that a call to setTimeout() is non-blocking. It doesn't stop or pause the running of the current thread of execution. In fact that thread of execution will run to completion. Instead, setTimeout() schedules a callback function to be called at a particular time in the future (when no other Javascript code is running).

Because Javascript is single-threaded, it pretty much never works to loop to try to create a delay, even if you use a really long while() loop. The issue is that while you are looping, no other Javascript code can run so no other state can change so whatever loop condition you are waiting to change will not change while you are looping. This commonly just leads to a deadlock type of condition and eventually the browser realizes that the Javascript thread is "stuck" and will prompt to abort it.

Instead, you program with events and future callbacks. You set up an event or timer callback so it can be called at some future time when something happens and then you put the relevant code in that callback or event handler. There are also more advanced tools to help manage this such as promises or async libraries, but they're just built on top of the same callback mechanism.

Boater answered 5/4, 2015 at 8:42 Comment(2)
setTimeout(setInterval(_=>{functionality}, delayTime), delayTime) doesn't work for me. Is there a way to get this to work?Chronicles
@mistarsv - You have to pass setTimeout() a function. You're executing setInterval() immediately and passiong setTimeout() the timerID that setInterval() returns. Wrap your setInterval() inside another arrow function so you're passing that arrow function to setTimeout().Boater
N
1

You can't really do Delay in javascript without having a callback.

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

}

Ideally your animation method would have a callback like "completed" or similar

Net answered 5/4, 2015 at 8:42 Comment(0)
C
1

Glad that you've got your issue sorted. :-)

I'll post my "2-cents" since I'm a big fan of using a single requestAnimationFrame loop to create animations.

  1. Create an array of javascript objects that represents each individual step in your timeline

  2. Sequentially "play" each step in a single requestAnimationFrame loop.

Here's an annotated example and Demo:

// 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>
Collette answered 5/4, 2015 at 18:39 Comment(1)
Thanks! This will come in handy for me!Luxuriant
N
0

I find it cleaner to set the function and do whatever in it, then pass the delayed action to an external setTimeout() as shown below:

function delayedFn() {
   //whichever to do here.. 
}
  
 
    //set the timing.. 
    setTimeout(delayedFn(){
    }, 10000);
Notice answered 5/4, 2015 at 8:48 Comment(0)
S
0

You could even get a rid of the timeout.

This prevents starting your start() function before the previous one finishes.

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();
Sleight answered 5/4, 2015 at 8:51 Comment(0)
P
0

This code waits for initialDelay seconds for first execution and thereafter executes every delay seconds

const initialDelay = 3
const delay = 5
let interval

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

you may or may not want to use clearInterval(interval) to stop repeating execution at some point or on destruction

Pellitory answered 20/5, 2021 at 3:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.