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?
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?
Share Improve this question edited Jul 17, 2021 at 21:46 Brian Tompsett - 汤莱恩 5,89372 gold badges61 silver badges133 bronze badges asked Mar 19, 2012 at 22:04 Timothy003Timothy003 2,3765 gold badges28 silver badges33 bronze badges 11- 2 I guess the real question would be: "what does the standard call for." – Jeffrey Sweeney Commented Mar 19, 2012 at 22:11
-
1
I think it's better to think of the "pausing" that happens with some built-in behaviors (
alert()
etc) as taking the form of the interpreter forcing a "yield" from the function running at the time, and then, when the "pause" is finished, resuming execution. In other words, thinking of the function as having actually returned while the "pause" is in progress, and then being continued as a continuation is continued. That way, the event handling makes a lot more sense, as it's just like ordinary event handling. – Pointy Commented Mar 19, 2012 at 22:18 - 7 Getting an "unresponsive script" prompt is hardly a normal thing you should design for. Your app is broken is the user ever gets one of those dialogs. You should design your scripting so the "unresponsive script" prompt never occurs and then you won't have to worry about re-entrancy. It does appear to me to be a somewhat minor bug that a major browser allows events to fire when in that prompt, but I think it's much more important to fix your code so the prompt never occurs and this is never an issue. – jfriend00 Commented Mar 19, 2012 at 22:18
- 1 @jfriend00 The problem isn't that my script is long-running; it's the idea that a browser can arbitrarily interrupt running code and execute a user callback. Of course, that idea would have no basis if the Unresponsive Script behavior turned out to be a bug, but we don't know that. If you do, please submit that as an answer. :) – Timothy003 Commented Mar 20, 2012 at 3:26
- The answer you linked to is pretty clear, isn't it? If it helps, think of it like this: in order to display a dialog, the browser's event loop needs to be running. In theory, the window's message queue could be plugged for scripting while pumping for everything else, but that's harder than it sounds when the entire browser UI is XUL and JS. – ephemient Commented Mar 20, 2012 at 3:30
2 Answers
Reset to default 2So 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/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.
I think that the problem es 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.