问题
While debugging some client side javascript today in Firefox, I ran into something that I found quite odd and little unnerving. Also, I was unable to duplicate this behavior while debugging the same script with IE / VS2010.
I created a simple example html document to illustrate the anomally I am seeing.
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.js" type="text/javascript" ></script>
</head>
<body id="main_body">
<script type="text/javascript">
$(function () {
$(".test-trigger").on("click", function () {
loadStuff();
console && console.log && console.log("this will probably happen first.");
});
});
function loadStuff() {
$.get("http://google.com/")
.fail(function () {
console && console.log && console.log("this will probably happen second.");
});
}
</script>
<button class="test-trigger">test</button>
</body>
</html>
If you load this document into Firefox (I am using version 13.0 with Firebug version 1.10.1 on Windows 7), click test, and view the console tab in Firebug you should notice that the get request fails (cross domain violation that has nothing to do with the point I'm trying to make here), and then you will most likely see:
this will probably happen first.
this will probably happen second.
Now, place breakpoints on lines 13 and 20:
13: console && console.log && console.log("this will probably happen first.");
20: console && console.log && console.log("this will probably happen second.");
If you click test again you will break on line 13 as expected. Now, resume execution. If your experience is like mine, you will not break on line 20. Also if you switch to the console tab you will see the following sequence of log output:
this will probably happen second.
this will probably happen first.
To me, this suggests that the fail handler of the ajax request is being executed in a thread other than that which the click handler is being executed in. I have always been led to believe that all the javascript for a single page will be executed by a single thread in any browser. Am I missing something really obvious here? Thanks for any insight on this observation.
Oh, if I debug the same page running in IE using Visual Studio, both breakpoints are hit as I would expect.
回答1:
I think it's safe to assume that the anomaly you're observing is caused by how Firebug implements breakpoints/works under the hood. I can't confirm that though. This also happens with FF 14 on OS X.
Unless jQuery immediately executes your fail() function and surpasses the whole XMLHttpRequest object, then there is a guarantee that the ordering of the statements will be this will probably happen first. then this will probably happen second..
Given the single threaded nature of JavaScript, functions will be essentially atomic; they will not get interrupted by a callback.
It seems as though you're trying to simulate what would happen if the click function takes a while to finish executing after calling loadStuff(). The click function shouldn't get interrupted by the fail method executing (mind == blown that you found a way to make that happen).
To take breakpoints out of the equation, here's a modified version. The rest of the markup is unchanged.
$(function () {
$(".test-trigger").on("click", function () {
loadStuff();
for (var i = 0; i < 1000000000; i++)
{
//block for some interesting calculation or something
}
console && console.log && console.log("this will probably happen first.");
});
});
function loadStuff() {
$.get("http://google.com/")
.fail(function () {
console && console.log && console.log("this will probably happen second.");
});
}
The click function clearly takes a long time to execute, after calling loadStuff(), but the console will still reflect the correct order of the log statements here. Also worth noting, if you insert the same breakpoints, the ordering will be invalid, like the original example.
I'd file an issue for this with Firebug.
回答2:
$.get("http://google.com/") is asynchronous, it is a race on what gets done first. The first time it is slower since it needs to make the call and the call happens later in the code execution. The call is already cached with the second request, so it happens to execute quicker.
If you need something to be done before the request goes out use beforeSend().
回答3:
To my experience, Firebug doesn't work well when putting breakpoints in asynchronous code.
I.e. if you have one straight line of execution and put breakpoints in it, you'll be fine. However if you introduce asynchronicity e.g. by using setTimeout, you won't hit the breakpoint in that "parallel" line (which of course is not parallel really, the JS engine switches between the tasks). I've been experiencing it a lot in last couple of months.
In Chrome, it seems to work fine (they defer timeouts intelligently somehow). Perhaps because Chrome dev tools are built-in to the browser, it's easier to manipulate the timeouts. Firebug is "just" an add-on and perhaps it may be tricky to do it correctly.
A simple script to reproduce the issue:
Put breakpoints in lines when I assign value to x, y, z.
First, you'll hit a breakpoint on the line x = 1. Use F10 to step over. You won't hit a breakpoint on the line with will hit a breakpoint on the line with z = 3 everz = 3 only if you're quick enough with pressing F10 (Firefox 14, Firebug 1.10).
<!DOCTYPE html>
<html>
<body>
<script type="text/javascript">
function foo(){
var x = 1;
setTimeout(bar, 2000);
var y = 2;
}
function bar(){
var z = 3;
}
foo();
</script>
</body>
</html>
来源:https://stackoverflow.com/questions/11802771/firebug-1-10-1-suggests-javascript-is-not-confined-to-a-single-thread-in-firef