0

JavaScript doesn't actually have a sleep() function corresponding to what is available in other languages. We can easily implement one though. The following code uses afunc as an example function where we want to be able to have the code sleep for a number of milliseconds between sections of code. The allowSleep() method allows us to implement this simply by calling sleep() the same as in other languages provided that we do not try to sleep in the middle of a loop and provided there is no code after the end of the function - it rewrites our function for us to implement the sleep delays without having to manually rewrite the code and without locking up the browser (as setting up a loop or using Java, Flash, or a server side language called from JavaScript to do the sleep would require).

You can substitute any function for afunc() in this example and have the code work exactly as shown provided that the sleep() calls are not in a loop.

Edited by peter_budo: Marking it as code snippet

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();
9
Contributors
21
Replies
27
Views
5 Years
Discussion Span
Last Post by Troy III
1

As far as I can tell, this is just a heavily disguised setTimeout().

Two points :

  • Anyone who's been writing javascript for a while should be able to write a setTimeout() for themselves.
  • There's a danger in disguising the setTimeout()that people will think this is a classical sleep - ie. that code immediately following an .allowSleep() call will wait for the sleep to expire.

It is a feature of javascript's setTimeout() that it fires from the global scope (exploiting a closure formed by the calling function if necessary) and that the remainder of the thread (not just the immediate function) from which setTimeout() was called is guaranteed to complete before the delayed function executes. It is a very good idea not to hide this effect from unwitting programmers.

Edited by Airshow

1

This was posted as a "Code Snippet" so why would you expect a question.

It isn't a disguised setTimeout - it is a setTimeout - just one where instead of you haveing to rewrite the function in order to incorporate the timeout the JavaScript does it for you.

I am aware of the limiitations mentioned - they are listed in the original post.

The big benefit this particular solution is that you can easily turn the sleep() calls on and off by commenting and uncommenting the allowSleep call. You can also use it with dozens of hundreds of different functions without having to rewrite them yourself.

Edited by felgall

0

I selected the code snippet forum before posting this and it appears to have ended up in the wrong place - can a moderator please move it to where it belongs - This is a supposed to be a JavaScrip Code Sniippet post.

0

I appreciate what you're trying to do here, but string function rewriting like this is pretty scary to me, especially when a random library function I'm using is doing it.

Here's an example of some code where I, with my Java hat on and Javascript hat off, would expect to be able to use sleep(), but could not:

function bfunc()
{
    var node = document.getElementById('text');
    var i = 100;
    while (--i)
    {
        sleep(4000);
        node.innerHTML = i;
    } 
}

When allow sleep is called on this function, it yields:

function bfunc()
    {
        var node = document.getElementById('text');
        var i = 101;
        while (--i)
        {
            bfunc.f1 = function() {bfunc.func1();};
sleep(4000,bfunc.f1);
bfunc.func1 = function() {
            node.innerHTML = i;
        } 
    }
}

When I run this code, my loop completes very quickly, having spawned all those settimeouts, then 4 seconds later, my text node's innerHTML is set to 0 100 times.

0

I did specify at the start that it doesn't work when the sleep call is inside a loop. Getting the script to rewrite itself to cater for that would be a lot more difficult - if it is possible at all.

0

can anybody explain to me what is the point of having a sleep function in JS, or in any other language for that matter?

1

Hi Troy,

Some older languages like Fortran and Basic were never intended for real-time event-driven applications but ended up being used for such. 'Sleep' seemed like a reasonable fix at the time (1970s) and probably was, given that these languages generally ran under operating systems that could only run one application at a time with interactivity limited to data input. There was virtually nothing else to be inconvenienced while sleep waited to time-out.

As you well know, setTimeout is a much more mature solution and most people master it quickly. Others, it seems, think that emulating sleep is a good idea but any attempt to do so will either hog the processor or, as here, will be a disguised setTimeout.

In my opinion, there's no point.

I trust all is well with you.

Airshow

Edited by Airshow

0

Well, thanks for sharing this snippet. Heheh.. I will have a look at it later and see how useful it is.

0

@rotten69
Then don't comment yet?

About the code: I don't see an added benefit over setTimeout. The distinguishing behavior of sleep functions is that they pause code execution. The problem that some people have with setTimeout is that it does not. I am sorry to say but as far as I can see, your code does not behave like a sleep function either and therefore I don't see a use for it?

Your example could easily be rewritten using the native setTimeout, I really don't see any added benefit for using your code here:

var arrayTexts = new Array('first message', 'message the second', 'last message');
var intervalTime = 5000;

function showTexts(arrayTexts, intervalTime){
    for(i = 0; i < intervalTime * arrayTexts.length; i+=intervalTime){
        setTimeout('changeText("'+arrayTexts[i/intervalTime]+'")', i);
    }
}

function changeText(newText){
    document.getElementById('text').innerHTML = newText;
}

Good things about this are:

  1. No eval()
  2. Clear and short native code
  3. No eval()
  4. No eval()!!!

Edited by McVaio

0

Many people think that setTimeout() is a sleep for the caller. No, it is not. To me, it likely works as a fork (threading) but not quite.

Looking at the script in the post above, the function will fire each process for each i value. Then each process will wait in the poll (and may not be in order) until the waiting time is expired. The result would be to call changeText() at almost the same time for every process.

When you attempt to imitate a sleep() function, you should tie each process in linear fashion. If the value required in the function is an array, make it global to that scope because you cannot pass an array structure liked via setTimeout() call (could pass as string with delimiter though).

var arrayTexts = new Array('first message', 'message the second', 'last message');
var intervalTime = 5000;

function showTexts(intervalTime) {
  changeText(0, intervalTime);  // start the loop process
}

function changeText(index, intervalTime){
  // checking for index & interval time,
  // but not check for arrayTexts type! (fail if arrayTexts is not an expected data type)
  if (!isNaN(index) && index>=0 && index<arrayTexts.length &&
      !isNaN(intervalTime) && intervalTime>0) {
    var idx = parseInt(index,10);  // ensure integer
    document.getElementById('text').innerHTML = arrayTexts[idx];
    setTimeout("changeText("+(idx+1)+","+intervalTime+")", intervalTime)
  }
}

One more thing about using setTimeout(), be careful when use it with action listener if you need to pass in dynamic values, such as keyboard event, mouse event, etc. You need to rewrite it as a closure function instead or you will get a wrong value passing into the function you are calling.

Edited by Taywin

0

Good things about this are:

1.No eval()
2.Clear and short native code
3.No eval()
4.No eval()!!!

I would agree with the point 2,
if it were the 1st.
And only.

0

The only meaningful response so far in this thread is Troy's question about the purpose of having a sleep function in JavaScript. Airshow answered it in general terms but didn't relate it back to my original code snippet - which is only using sleep because it results in about 1/4 of the code that is required to use the technique in an actual proactical way.

There is no purpose to a sleep function in JavaScript - JavaScript doesn't need a sleep function. That is not the point of the code snippet at all.

I have posted this same code snippet in three different forums and so far have had only one response from a programmer (who provided a slightly rewritten version that made it slightly easier to see how some parts of the code worked).

For those learning to be programmers I will list some of the purposes that this particular code snippet serves. I also suggest that you read the Nostarch Press book "Think Like a Programmer" that was recently written by V. Anton Spraul which explains tghe basics of the bigger part of what a programmer needs to know that most people who think they are programmers have never learnt - problem solving.

  1. The snippet is a non-trivial examply of attaching methods to the Function object - one of the few uses for directly referencing that object.
  2. It demonstrates what is possibly the only legitimate use for the eval() command - for where you have a function that you have converted to a string and then need to convert back to a function - while there are many good reasons for avoiding the use of eval() none of them apply in this particular situation and there is no alternative way to write that particular piece of code - at least no one has been able to provide me with one).
  3. It demonstrates the flexibility of regular expressions in JavaScript providing an example of how you can use a function as the replacement value.

The problem is NOT how to replicate a sleep() function in JavaScript - the real problem is MODAL DIALOGS. This code snippet is a partial solution to how to replace the JavaScript debugging commands alert, confirm and prompt with your own custom versions that do not have the debugging options added into the dialog.

0

Felgall, thank you for your explanation.

I now understand that this code snippet entitled "JavaScript Sleep" and illustrated with an example is actually about Modal Dialogs and illustrated with a custom method of the Function object.

Several questions arise:

  • Why not give the code snippet an appropriate title and phrase the introductory text accordingly?
  • Is new Function(...) not an alternative way to write the eval() piece of code?
  • Are "modal dialogs" not dialogs that, until accepted or canceled, inhibit interaction with a main user interface? If this definition is correct, then it's difficult to see how sleep() might apply.

Many years ago I wrote some code that string-handled a function in order to extend its functionality. That code was a product of gross inexperience and something of which I became very rapidly deeply embarassed.

In javascript, as in other languages, much can be achieved with "design patterns". In this regard, you might like to be aware of the "CommonJS Promises/A" design and "Observer" patterns, both of which are (I think) at least of peripheral relevance to this topic. As far as I'm aware, no implementation of these patterns relies on string-handling function code. These and other design patterns were devised by and are used by programmers.

Edited by Airshow

0

It isn't just about modal dialogs. The particular code in that example is the ONLY way to even partly implement the equivalent of the sleep() function that you find in some other languages in JavaScript.

The same approach with the same limitations is the only way that you can override any of the debugging modal dialogs in JavaScript with your own custom versions without having to rewrite the code that calls them - see http://www.felgall.com/jstip113.htm for a way to implement a replacement for alert() - which gives pieces of code over four pages to cover all the possibilities and has a link to a working example where the alert and some text at the top of the page are updated alternately.

Having functions able to rewrite themselves is the only legitimate use for the Function object that JavaScript provides. The sleep example is about the simplest such rewrite that it can do - since that Function method contains only two statements. If you do a search for Memoize you will find another useful method for rewriting functions so that they don't have to be rerun when called with the same parameters as a prior call.

I would expect to find similar functionality in any Prototyping language but not in any language that uses a less flexible approach such as object oriented. You'd need to find another prototyping language if you want to do a legitimate comparison of coding techniques - many of the common object oriented design patterns are the long way of coding in JavaScript because they limit themselves to an object oriented approach and don't take advantage of JavaScript being a Prototyping language.

If you can't see how the sleep code applies to modal dialogs then please show the code for a custom implementation of alert() that can be used with the following calls to it with the updates and the dialog display occurring in the correct order

afunc = function() {
  document.getElementById('text').innerHTML = 'first message';
  alert('one');
  document.getElementById('text').innerHTML = 'message the second';
  alert('two');
  document.getElementById('text').innerHTML = 'last message';
}
afunc();

Or alternatively provide an implementation of sleep() in JavaScript that can be coded the same way that the PHP sleep function is coded that doesn't use any system resources during the sleep.

This particular JavaScript technique doesn't have very many actual applications and it can't handle all the possible situations but it is as close as you can get to implementing sleep() or a custom alert() in JavaScript without having to completely rewrite the entire JavaScript every time you want to insert such a command.

Another use for this style of coding would be to replace the eval with a call that outputs the rewritten function to the web page and then use the code to rewrite functions for you. You could then implement the rewritten functions on your live site rather than having the script rewriting the functions dynamically. It would certainly make implementing a custom alert into a script that contains hundreds of existing alert calls a lot easier as the JavaScript would be rewriting a lot of the code for you.

Anyway, I have seen a lot of queries on various forums with people asking how to implement sleep(0 or a custom modal dialog in JavaScript and this is the only approach that even partly works without significant manual rewrites of the code. The code also demonstrates being able to update the way functions work dynamically by adding methods to the Function object and also how the String.replace method can use a function as its second parameter in order to do complex updates to a string from within a single call.

Edited by felgall

0

Alright, so, in addition to not working for loops, more tests with your original post code also seem to show that sleeps cannot be called (correctly) inside an if statement. I tried:

afunc() {
...
    if (true) {
        sleep(5000);
    }

    txt.textContent = 'Done!';
...
}

which turned into

afunc() {
...
if (true) {
    afunc.f4 = function() {afunc.func4();};
    sleep(5000,afunc.f4);
}
afunc.func4 = function() {
}

txt.textContent = 'Done!';
...
}

When that runs, as you can tell, the setTimeout will call an empty function 5 seconds later, but the txt.textContent will get set right away. The other thing I noticed is that it seems, in my tests at least, the this object can potentially change due to the sleeps, depending on how the function is called. If I call window.afunc(), this is going to be the window, but after calling afunc.func4(), this will become afunc. While this is probably not an extraordinarily large issue, I think it is important. As a side note, //sleep(123123) is still treated as a real sleep call and replaces some parts, but one line of the replacement gets commented out, so users would need to be careful when commenting out a sleep.

One more thing is that it doesn't (and I think you mention this yourself in a way) halt the execution flow and therefore doesn't prevent the function from returning. Calling x(afunc()) will not cause x to get the expected result from afunc. However, that's not a huge deal since the way the sleeps are converted to setTimeouts will prevent the return call from actually making it's way out of afunc, meaning the afunc couldn't return anything if it wanted to.

To reenumerate, the original code:

  1. Can't be called in a loop
  2. Can't be called in a conditional statement
  3. Can, in some cases, unexpectedly change this
  4. Doesn't account for sleeps within comments
  5. Prevents the function from returning anything
  6. Doesn't actually stop execution flow

Given that, and in an attempt to be more constructive and not just critical, can we fix these issues and make a working sleep? For issues 1 to 4, the answer is definitely yes. I'll address that in a second. As for 5, I think the answer is kind of, but we have to do it in a round about way. Java Futures come to mind where afunc would return a handle. The receiver of that handle doesn't have to check the value at any specific time. By the time they do ask for handle.checkValue(), afunc could have finished and the value could be there. That's the ideal case. The most likely case, though, is that you'd want to check that handle much sooner, and you'd either have to provide a callback or spinwait. That we're having to change downstream code because of the sleep kind of defeats the purpose. For number 6, as far as I know, there is no possible way to stop execution flow in Javascript except alert, prompt, and debugger, none of which get us where we want to be. The only solutions to 6 are rewrite your code manually or use large amounts of black magic and rewrite the whole js app automatically. Lots of functions can run asynchronously in Javascript, though, and they don't typically provide return values, so we can (acknowledging the caveats of #5 and #6) press on trying to resolve 1-4.

3 (Losing this at times) is really easy to fix. We simply call any autogenerated functions by either .bind(this) or .call(this) as needed. This should retain the intended this in most if not all cases.

4 (sleep in single line comments) is also really easy. We can easily preprocess using something like f = f.replace(/\/\/[ \t]*sleep\s*\(/gi, "//nosleep("); or you could force your sleep finding regex to check for whitespace, not just word boundary before a sleep call.

As for looping and conditionals, I took a few minutes and started to write something along these lines. My goal was just to make sleep work both in and out of for(x;y;z) style for loops, putting aside all the other complexities. Using a regex like /\bfor\s*\(([^;]*);([^;]*);([^\)]*)\)\s*{/gi I was able to grab the head of each for loop, then count braces to find the end of the loop. Not a big deal.
With a little more magic, I found I could turn a for loop like

this.argleBargle = 42;
for (var i = 0; i < 8; i++) {
    // 1
    console.log("test");
    sleep(503);

    // 2
    txt.textContent = "Iteration: " + i;
    console.log(this.argleBargle++);
    sleep(1504);

    // 3
    if (i === 3) {console.log("continuing"); continue;}
    if (i === 5) {break;}
    txt.textContent += ".5";
}
// Other code here

into

this.argleBargle = 42;
var i = 0;  
var for2 = function for2() {
    if (i < 8 && !for2.__breaked__) {

        // 1
        console.log("test");

        setTimeout(function () {
            // 2
            txt.textContent = "Iteration: " + i;
            console.log(this.argleBargle++);
            setTimeout(function () {
                // 3
                if (i === 3) {
                    console.log("continuing");
                    i++;
                    for2.call(this);
                    return;
                }
                if (i === 5) {
                    for2.__breaked__ = true;
                    for2.call(this);
                    return;
                }
                txt.textContent += ".5";
                i++;
                for2.call(this);
            }.bind(this), 1504);

        }.bind(this), 503);
    } else {
        // Other code here
    }
};
for2.call(this);

(I've altered the indents for readability. The actual produced code is not indented as nicely.)

So, that's all well and good. It works like I'd expect. Any sleep() call inside a for loop works great. Then come the challenges. Unfortunately, I couldn't concoct a way to have the for loop not use an if in its design, so doing for before if was a poor choice. There's also the fact that with these added functions wrapping everything, we have to be really careful that sleeps which were not actually within an original for loop but are now in a function must only wrap things also in that function. Your original code does, as far as I can tell, deal correctly with sleeps called in inner functions, but it's still something that has to be verified again here.

Lastly, the issue of conditionals would have to, to my knowledge, be dealt with by taking code like

if (cond) {
    //a
    sleep(1000);
    //b
}
else {
    //c
    sleep(2000);
    //d
}
// e
/*END*/

and replacing it with

var e = function(){
    //e
}

if (cond) {
    //a
    setTimeout(function(){
        //b
        e();
    }, 1000);
}
else {
    //c
    setTimeout(function() {
        //d
        e();
    }, 2000)
}
/*END*/

But I haven't quite tried to tackle that yet. In the end, it really seems like the better way to go about this is to use a javascript parser like the one with uglify.js where you can really get into the meat of the syntax tree in order to re-write it rather than trying to do so at a string level. Still, this could only ever, at best, be an asynchronous tool unless you get into the business of rewriting all the js on the page, and in the end, people are probably better off just using setTimeout natively.

P.S. Here's the code I've currently got matching the for loop stuff as described

var forLoopParser = function(f) {

    var forLoopMatcher = /\bfor\s*\(([^;]*);([^;]*);([^\)]*)\)\s*{/gi,
        newText = "",
        lastMatch,
        sleepMatcher = /\bsleep\s*\(\s*(\d+)\s*\)\s*;?/gi,
        forLoopReplacerIndex = 0;


    // Find all the for loops
    sleepMatching:
    while (forLoopMatcher.test(f)) {
        forLoopMatcher.lastIndex = 0;
        lastMatch = undefined;
        while ((match = forLoopMatcher.exec(f)) !== null) {
            var braceCount = 1,
                start = match.index,
                end = match.index + match[0].length,
                prevCharAt,
                charAt,
                singleQuoteMode = false,
                doubleQuoteMode = false;

            while ((charAt = f.charAt(end)) && braceCount) {
                if (prevCharAt === "/" && charAt === "/") {
                    end = f.indexOf("\n", end);
                }
                else if (prevCharAt === "/" && charAt === "*") {
                    end = f.indexOf("*/", end);                
                }
                else if (prevCharAt !== "\\" && charAt === "\"" && !singleQuoteMode) {
                    if (doubleQuoteMode) {doubleQuoteMode = false;}
                    else {doubleQuoteMode = true;}
                }
                else if (prevCharAt !== "\\" && charAt === "'" && !doubleQuoteMode) {
                    if (singleQuoteMode) {singleQuoteMode = false;}
                    else {singleQuoteMode = true;}
                }
                else if (charAt === "{" && !doubleQuoteMode && !singleQuoteMode) {
                    braceCount++;
                }
                else if (charAt === "}" && !doubleQuoteMode && !singleQuoteMode) {
                    braceCount--;
                }
                prevCharAt = charAt;
                end++;
            }

            // If the loop contains a sleep
            sleepMatcher.lastIndex = 0;
            if (sleepMatcher.test(f.substring(start,end))) {
                lastMatch = {
                    init: match[1], 
                    cond: match[2], 
                    cont: match[3],
                    start: start,
                    end: end,
                    beforeText: f.substring(0, start),
                    loopText: f.substring(start, end),
                    loopBodyText: f.substring(match.index + match[0].length, end-1),
                    followOnText: f.substring(end)
                };
            }
        }

        if (typeof lastMatch !== "object") {
            break sleepMatching;
        }

        var forIndex = ++forLoopReplacerIndex;
        var forName = "for" + forIndex;

        newText = lastMatch.init + ";";
        newText += "\n\t" + "var " + forName + " = function " + forName + "() {";
        newText += "\n\t" + "if (" + lastMatch.cond + " && !" + forName + ".__breaked__) {";
        newText += "\n\t" + lastMatch.loopBodyText;

        newText = newText.replace(/\bbreak\b/, forName + ".__breaked__=true; " + forName + ".call(this); return");
        newText = newText.replace(/\bcontinue\b/, lastMatch.cont + "; " + forName + ".call(this); return");

        var lastSleep, lastSleepCount = 0;
        while (sleepMatcher.test(newText)) {
            sleepMatcher.lastIndex = 0;
            while ((sMatch = sleepMatcher.exec(newText)) !== null) {
                lastSleep = {
                    start: sMatch.index, 
                    end: sMatch.index+sMatch[0].length, 
                    time: parseInt(sMatch[1]), 
                    otherParams: sMatch[2],
                    followOn: newText.substring(sMatch.index+sMatch[0].length)
                };
            }
            var newSleep = "";

            newSleep  = "\n\t\t" + "setTimeout(function() {";
            newSleep += "" + lastSleep.followOn.replace(/(?:^|\n)\s*/g, "\n\t\t\t");
            newSleep += "\n\t\t\t" + "";
            if ((++lastSleepCount) === 1) {
                newSleep += "\n\t\t\t" + lastMatch.cont.replace(/^\s*/g, "") + ";";
                newSleep += "\n\t\t\t" + forName + ".call(this);";
            }
            newSleep += "\n\t\t" + "}.bind(this), " + lastSleep.time + ");";

            newText = newText.substring(0, lastSleep.start) + newSleep;
        }

        newText += "\n\t}";
        newText += "\n\telse {";
        newText += lastMatch.followOnText;
        newText += "\n\t" + "};";
        newText += "\n\t" + forName + ".call(this);";
        newText += "\n\t}";


        f = lastMatch.beforeText + newText;

    }

    return f;
}

Edited by tigerofdoom: minor optimization

0

Tigerofdoom,

I think the OP should be very flattered that you have taken his idea seriously and found the time to move it forward. I'm sure he will have some valuable feedback for you.

Meanwhile, here are a few possible improvements :

"... except alert, prompt, and debugger ..."

Don't forget confirm(). Also, maybe worth noting that debugger() is not included in the javascript specification but is included in all modern, major browser implementations. I'm not sure it's included in non-browser imlementations (eg NODE).

".bind(this) or .call(this)"

Don't forget .apply(this). Also, maybe worth noting that .bind(this) is only available from ECMA-262 5th edition.

String building

In javascript as in many languages, building a string with += is horribly inefficient. Javascript doesn't have a native stringbuffer but a composite string can be efficiently built with a series of simple + operators and/or in an array, which is finally joined together.

var newSleep = [];
newSleep.push('...');
newSleep.push('...' + '...');
newSleep.push('...');
newSleep = newSleep.join('');

General

If people want to do this sort of thing in eg. Greasemonkey that's up to them but I'm not sure I would ever want voluntarily to trust my hard-crafted code to a bunch of clever regexes just for the sake of a sleep() function which I am not (yet) persuaded is of anything more than academic interest.

For me, the "realpolitik" of this is that point 6 is a killer, and manually reorganising the code to achieve asynchronicity is the only realistic way ahead.

Edited by Airshow

0

I still cannot find an adequate use for sleep/wait or any command of the kind, on any possible scenario.
Can anybody find me one?

Have something to contribute to this discussion? Please be thoughtful, detailed and courteous, and be sure to adhere to our posting rules.