Why do I see duplicate javascript async responses?
I'm trying to understand why I'm seeing the following:
I fire off several async requests inside a for loop and then simply print out the responses. Here's a very simplified version of what I'm doing:
function getStuff(){
var singers = ['marley','matthews','johnson','buffett'];
for(lastname in singers){
var lastName = singers[lastname];
log("Making request for "+ singers[lastname]);
makeAsyncRequest(singers[lastname], function(response){
//this is the callback when the async request returns
log("RESPONSE - "+lastName+": "+response);
});
}
}
If I run the code with syncronously (async: false), I get the following results as expected every time:
Making request for marley
Making request for matthews
Making request for johnson
Making request for buffett
RESPONSE - marley: bob
RE开发者_JS百科SPONSE - johnson: jack
RESPONSE - matthews: dave
RESPONSE - buffett: jimmy
When I run asyncronously, I see results like the following. I understand that the responses might come back out of order. But what I don't understand is when they come back out of order, why do I see duplicate values? For example, I might see:
Making request for marley
Making request for matthews
Making request for johnson
Making request for buffett
RESPONSE - marley: bob
RESPONSE - johnson: dave
RESPONSE - matthews: dave
RESPONSE - buffett: jimmy
Notice 'dave' seems to be returned twice? I would understand if the values 'jack' and 'dave' were switched, like so:
Making request for marley
Making request for matthews
Making request for johnson
Making request for buffett
RESPONSE - marley: bob
RESPONSE - johnson: dave
RESPONSE - matthews: jack
RESPONSE - buffett: jimmy
But I don't understand why the "response" param in the callback appears to be getting set to the same value twice? Am I overlooking something basic here?
Thanks for any help!
A loop does not have its own scope. The problem is, that there is only one lastName
variable and when the async request returns its content will not always be the content you assigned to it in the iteration.
You should read and understand how the scope in JavaScript works.
A solution could be an additional closure in your loop:
function getStuff(){
var singers = ['marley','matthews','johnson','buffett'];
for( lastname in singers ) {
function( lastName ) {
log( "Making request for "+ lastName );
makeAsyncRequest( lastName, function(response) {
//this is the callback when the async request returns
log("RESPONSE - "+lastName+": "+response);
} );
}( singers[lastname] );
}
}
try to keep a copy of the lastname in the for loop :
function getStuff()
{
var singers = ['marley','matthews','johnson','buffett'];
for(lastname in singers)
{
var currentName = lastname;
log("Making request for "+ singers[currentName]);
makeAsyncRequest(singers[lastname], function(response)
{
//this is the callback when the async request returns
log("RESPONSE - "+singers[currentName ]+": "+response);
});
}
}
You've got a race condition. The singer[name] value inside your response handler depends entirely on the state the for() loop is in. Once you're doing async requests, it's entirely possible to get out of sync if any of the requests get delayed (net.burp, slow server, etc...).
In this case, the remote rest service required basic authentication. I was sending a different username and password each time thru the loop. I finally realized that the basic auth credentials were getting cached on the server side.
On the server side, if the service found a valid cookie named JSESSIONID, it ignored the Basic Auth username and password in the http request header.
So, the solution was to use the Jquery cookie plugin and make sure to remove the JSESSIONID for each request like so:
$.cookie('JSESSIONID', null {path: '/pathToCookie'});
精彩评论