0

I've done the research, so I appreciate that there's no sleep() method in javascript. What I can't figure out is what is the correct alternative for me, because setTimeout() isn't solving my situation due to its asynchronous nature.

My program parses input from users and executes it in an eval() statement. The executing code will call functions that I've pre-written and made available to the users. These functions include draw(color), which will place a colored square on the screen at a spot defined by the user. It's not necessarily relevant for the question, but in case you want to know I'm just making a <div> and inserting a semi-transparent <img> with the proper heights.

Now here's where I want the sleep function: If the user draws many squares, javascript displays them all at once, but I want to display them one after the next, with a delay I can control. This cannot be done with setTimeout(). Trying to self-write a delay function calculating start and end times will just freeze up the browser, and believe me I've tried it and it still draws everything at once after being frozen.

My instinct is to put a sleep() call as the last line of my draw() method, so each square the user creates in their code will take e.g. 200 ms to render, but no such function exists.

Oh JavaScript gurus, how can I get this kind of functionality in a web page if javascript won't sleep?

ideally:

function draw() {
    var easel = document.getElementById("canvas");
    var square = document.createElement("div");
    square.setAttribute("...");
    easel.appendChild(square);

    window.sleep(200);
}
Mr Lister
  • 45,515
  • 15
  • 108
  • 150
Cephus5
  • 17
  • 1
  • 1
    could you use set interval, calling each function to draw after each interval, once all functions are called remove the interval – user2950720 Apr 01 '16 at 19:31
  • _“This cannot be done with setTimeout()”_ – that being totally untrue of course notwithstanding – if performance really is an issue, you might want to look into `requestAnimationFrame`. – CBroe Apr 01 '16 at 19:42
  • Mike indeed proved that setTimeout() was able to do what I needed, and without freezing the browser like virtually every other sleep hack will do (see the ostensibly 'duplicate' question linked above, where I couldn't find the right solution). So thank you all for showing me that there's more to setTimeout() than I originally thought. – Cephus5 Apr 03 '16 at 01:51

1 Answers1

2

Since you control the functions your users are calling, you could modify draw to place the squares in a queue. Then you slowly empty that queue, drawing one rectangle at a time.

Some pseudo-JS to describe the situation:

// Collection of things to render
var rectangles = [];

// This is the function you have users call for drawing a rectangle
function draw(color) {
  rectangles.push(new Rectangle(color));
  // do other stuff
  queueRender();
}

// Slowly draw each rectangle
function queueRender() {
  if (alreadyQueued) {
    return;
  }
  alreadyQueued = true;

  setTimeout(function drawQueue() {
    var rectangle = rectangles.shift(); // Get the first rectangle
    if (!rectangle) {
      alreadyQueued = false;
      return;
    }
    render(rectangle);
    setTimeout(drawQueue, 200);
  });
}
Mike Cluck
  • 31,869
  • 13
  • 80
  • 91
  • I think I follow where you're going with this, and I'll try it out - thanks in advance. What is the purpose of the nested setTimeout? You have one with only a function definition as argument "setTimeout(function drawQueue..." and then within that function another setTimeout with a recursive call and a 200 ms timeout "setTimeout(drawQueue,200);" - is the first one a typo? – Cephus5 Apr 02 '16 at 00:32
  • Thank you, Mike - this works and fulfills my requirements. I really thought it was impossible, hats off to you! – Cephus5 Apr 02 '16 at 01:03