Javascript return not breaking out of function
I have a javascript function which checks to see if an artist exists in an XML file:
function a开发者_C百科rtistExists(artist) {
// get data from artists.xml
$('.loading').show();
$.get(artists_xml, function(xml){
$('.loading').hide();
$(xml).find('artist').each(function(){
if ($(this).find("ar_artist").text() == artist.val()) {
alert ('artist exists');
return true;
} //end if
}); // end each
alert ('artist does not exist');
return false;
}); // end .get function
} // end of artistExists function
Am I right in thinking that the 'return true' line should quit execution of the function? I thought it would, but after finding a record and running the first alert execution continues to the failure alert at the bottom.
What am I doing wrong please? Thank you.
Yes, it does "quit" execution of the function. The question is, "which function?" In this case the answer should be pretty clear: it's the function passed to .each()
.
You can terminate the looping behavior of .each()
by returning false
instead of true
, but that still won't get you out of the outer function. What you should probably consider is to set up a local variable in the outer function, and have the inner function set that when it finds something (and then break the .each()
loop). Then the main function can check the local variable to see if it was set.
This is a case where I'd really like to use a .reduce()
or .inject()
API, but jQuery doesn't have one and they're really opposed to it.
Return false
, rather than true
, to terminate the each
loop; from the docs:
We can stop the loop from within the callback function by returning
false
.
That will just terminate your each
loop, though, not the overall function. You'll need to set a flag so you know whether you found something, e.g. something like this:
function artistExists(artist) {
// get data from artists.xml
$('.loading').show();
$.get(artists_xml, function(xml){
var found = false; // <== Added
$('.loading').hide();
$(xml).find('artist').each(function(){
if ($(this).find("ar_artist").text() == artist.val()) {
alert ('artist exists');
found = true; // <== Added
return false; // <== Modified
} //end if
}); // end each
if (!found) { // <== Added
alert ('artist does not exist');
} // <== Added
return found; // <== Modified
}); // end .get function
} // end of artistExists function
$.get
is an asynchronous function, that means, the main function, artistExists
will return immediately, and a GET request will be initiated. To be able to get the result you will need a callback.
function artistExists(artist, cb) {
$('.loading').show();
$.get(artists_xml, function(xml) {
var found = false;
$('.loading').hide();
$(xml).find('artist').each(function(){
if ($(this).find("ar_artist").text() == artist.val()) {
found = true;
return false; // use return false to stop .each()
}
});
// the built in action.
if (found) {
alert ('artist exists');
} else {
alert ('artist does not exist');
}
// call the callback function
cb (found);
});
}
Then to use, you need to use a callback function. From
var isExists = artistExists('lol');
// do stuff
You need to change it to:
artistExists('lol', function(isExists) {
// do stuff
});
Thanks for all the advice. In the end I decided I needed a synchronous call, so I made the following new version of the .get function, called .sget:
jQuery.extend({
sget: function( url, callback, type ) {
return jQuery.ajax({
type: "GET",
url: url,
success: callback,
async: false,
dataType: type
});
}
});
The 'async: false' pair in the 'ajax' options makes the call synchronous. Then the following edit of my original failing function:
function artistExists(artistname) {
var found = false;
console.log("From Input:Artist= " + artistname.val());
// get data from artists.xml
$('.loading').show();
$.sget(artists_xml, function(xml){ // new synchronous get
$('.loading').hide();
$(xml).find('artist').each(function(){
if ($(this).find("ar_artist").text() == artistname.val()) {
console.log('From File:Artist= ' + $(this).find("ar_artist").text());
found = true;
console.log("In each loop:Flag= " + found);
return;
} //end if
}); // end each
}); // end .get function
console.log("At end:Flag= " + found);
return found;
}
The console.log lines will be removed. They show though, that things are now happening in the order I want. SO the new synchronous .sget function and the use of a 'found' flag, as advised above, have done the trick for me. Don't know why I couldn't think of doing this yesterday.
Thanks everyone.
精彩评论