JavaScript Sleep

There is no sleep command in JavaScript and the only effective way to emulate one is to break the script into two parts at the point where you want the sleep to occur. Because of JavaScript's single threading nature any attempt to actually pause the currently running process basically locks out any other JavaScript from being able to run as no other JavaScript process can start until the currently running process finishes.

The only way to emulate a sleep function using only JavaScript is to set up a loop where the processing basically gets stuck in the loop until the specified sleep time has elapsed. Instead of releasing all resources the way a sleep function in some other language would, doing this actually increases the resources that our JavaScript process uses since it is processing the loop many times a second while waiting for the sleep time to elapse. Browsers generally have code built in to prevent one JavaScript process from running for too long and so if the sleep function were to run for more than a small fraction of a second there would likely be popups warning that a JavaScript process has been running for too long and do you wish to cancel it.

If Java or Flash is available in the browser then either of those could be called from the JavaScript in order to use the sleep function in either of those languages. Of course the sleep would then also be reliant on the other language being installed. To get around that issue we could use a server side language instead to provide the sleep and use a synchronous XMLHttpRequest call to the server to process the sleep (one where the third parameter to the open call is false so that the script waits for a response instead of continuing processing and using a readystatechange event to check for when the response is returned. Any of these approaches would prevent the JavaScript process from using all the CPU resources while waiting but the JavaScript process would still be waiting and so no other JavaScript processes would be able to run while we are waiting and we'd still get the popup warning about the process running for too long.

The only way to produce a workable sleep using JavaScript is to break the script into two separate processes where the first process completes when the sleep starts and the second process starts when the sleep ends. Only by doing this will other JavaScript processes be able to run during the sleep. Also because the process isn't running during the sleep we don't get the warning popups about long running processes.

So how do we split our process into two and tell JavaScript to wait for a period of time after the first process finishes before starting the second process? Well the setTimeout function built into JavaScript provides everything we need to be able to do this. Let's look at a simple example that has one statement representing all the code before the sleep and one statement representing all the code after the sleep.

myprocess = function(a, ary) {
  document.getElementById('a').innerHTML = ary[0];
  // we want to sleep for five seconds here
  document.getElementById('a').innerHTML = ary[1];
  }

To implement the sleep in JavaScript we need to split that function into two like this:

myprocess = function(a, ary) {
  document.getElementById('a').innerHTML = ary[0];
  setTimeout(function() {myprocess1(a, ary);}, 5000);
}
 
myprocess1 = function(a, ary) {
  document.getElementById('a').innerHTML = ary[1];
}

Note that we have split the one function into two immediately after the spot where we want the sleep to occur. The setTimeout function call is then added to the end of the first function in order to perform the actual sleep for us. The first parameter passed to the setTimeout identifies that second function we created that contains the code we want to run after the sleep and the second parameter is the number of milliseconds we want the script to sleep for.

The setTimeout basically queues the second process to be run after the specified time has elapsed and the first process ends as soon as the request has been queued. Our second function will then commence once the specified time has elapsed provided that no other JavaScript process is running at that time. If another JavaScript process is running then it will run as soon as that other process finishes.

With the built in prompt, confirm, and alert dialogs now being having additional options displayed in some browsers so as to make them easier to use for debugging you may decide to construct your own replacements for these dialogs. You might also decide to add your own additional dialogs that have completely different contents. When you do this your dialogs will have the exact same issue as above except that instead of needing a delay for a specific time you need a delay until a button in the dialog is pressed. The solution therefore for displaying your own dialogs using JavaScript is the same as where you want to emulate a sleep command in JavaScript except that the call to display your dialog substitutes for the setTimeout and the second function will need to be triggered when any of the buttons in your dialog are pressed (with the appropriate values being passed to it depending on which button is pressed.

The difficulty with applying this alternative is that you also need to restructure your code so that the point where you want the sleep or dialog is not inside a loop and that there is no further processing that will run automatically after the current function finishes. You also need to consider how local variables available before the sleep will be made available afterwards. There is no simple JavaScript solution to this where you want the sleep or dialog inside a loop or where you want it in a function that only contains a part of the current process.

One possible solution where you have a single function sequential process is as follows - the second last line turns on the sleep functionality for the specified function:

Function.prototype.allowSleep = function(n) {
   var i, f;
  i = 0;
  f = this.toString().replace(/sleep\((.*?)\);/g , function(f,t) {i++; return n+".f"+i+" = function() {"+n+".func"+i+"();};\nsleep("+t+","+n+".f"+i+");\n"+(i==1?"":"}\n")+n+".func" + i +" = function() {";
  });
  eval(n+'='+f+'\n}');
}
sleep = function(t,f) {
  if (f === undefined) return;
  setTimeout(f,t);
}
 
afunc = function() {
  document.getElementById('text').innerHTML = 'first message';
  sleep(5000);
  document.getElementById('text').innerHTML = 'message the second';
  sleep(5000);
  document.getElementById('text').innerHTML = 'last message';
   }
afunc.allowSleep('afunc');
afunc();

 

This article written by Stephen Chapman, Felgall Pty Ltd.

go to top

FaceBook Follow
Twitter Follow
Donate