Wednesday, May 2, 2012

can JavaScript flow of execution be interrupted?


I've always thought that, since JavaScript was single-threaded, I could attach event handlers without worrying about the handlers getting executed while I was in the middle of executing code. To my surprise, I found that they could. According to this answer, the 'Unresponsive Script' dialog box can cause events to be raised while the script is still running.



I tested this with the following code:




<script>
function loop() {
var cond;
onblur = function (event) {
cond = false;
};
cond = true;
while (cond)
;
alert('loop exited!');
}
</script>
<button onclick="loop()">loop()</button>



(jsFiddle)



In Firefox 11.0, the function prints "loop exited" after clicking continue. The loop seems to be paused, with events allowed to execute. This is akin to a Unix signal, which temporarily changes the context of the target thread. But it is much more dangerous, as it allows external state to be altered.



Is this a bug? Should I no longer depend on the single-flow-of-execution model of JavaScript and ensure that all my scripts are re-entrant? Or is it a flaw not worth pursuing despite a major browser allowing this to happen?


Source: Tips4all

2 comments:

  1. So yeah, if you create an infinite loop you will hang your JavaScript. Diff browsers will handle this differently. Firefox 11 for me throws up a window saying your script has hung. Chrome is just spinning for me at the moment.

    This should prove the point. Never calls alert() in FF11 or Chrome 17.

    while(true){}
    alert("sucks");


    http://jsfiddle.net/JCKRt/1/

    You asked about sync statements in JavaScript. There are a few other synchronous statements that will block the execution of JavaScript like alert(), confirm(), synchronous ajax calls and more.

    You usually you want to avoid any sort of synchronous stuff in JavaScript! If you want things to pause, you may want to re-think the design. JavaScript is event driven. You don't need it to spin in a while loop, because nothing on the page will work including any events like clicks, etc.

    ReplyDelete
  2. I think that the problem comes in when you start dealing with events.

    Rather than using a while loop, consider using a recursive function.

    Here are some modifications I made to your code that should execute as desired.

    <head>
    <script>
    function loop(that) {
    var cond;
    that.onblur = function (event) {
    cond = false;
    };
    cond = true;
    var loop = 0
    var xy = function x (){
    if(cond == true){
    loop++;
    setTimeout(function(){xy()},0);
    } else {
    alert('loop exited! - '+loop);
    return true;
    }
    }
    xy();
    }
    </script>
    </head>
    <body>
    <button onclick="this.focus(); loop(this)">loop()</button>
    </body>


    Using setTimeout() with a 0 delay should allow any events to jump in without any issues. Your loop code won't be executed nearly as quickly but you will just have to test it and see.

    ReplyDelete