function outer() {
$(['hi', 'there']).each(function(idx, e) {
console.log(e);
return;
});
alert("I don't want to be called");
}
function outer() {
$.get('http://test.com', function (data) {
console.log(data)
return; // I want to terminate the entire outer() function here.
});
alert("I don't want to be called");
}
What's the convention of breaking out of nested functions in cases like this? When using for loops, returning inside them terminates the entire function that encloses them. However, since $.each() is an individual function call, returning from it only ends itself, not the outer function. I could simply return twice, once inside and once outside in that case, but I am not sure how I would deal with $.ajax() because it is necessary to terminate the function only when I get a successful response.
Here is a summary of how it all works.
.each()
return true; // skips to the next iteration of .each()
return false; // exits the .each() loop
In short there's no way of breaking out of the function containing .each() in a single statement.
$.get()
return [true|false]; // makes no sense at all.
As the return in $.get() does not get executed until the ajax call is complete, it wont serve much purpose. Even when you make a synchronous ajax call, a return statement in the success callback still does not do anything substantive. Think of a return from a function as a value to be assigned to the calling statement.
What's making it necessary to break out of your functions?
For $.each(), you can stop the iteration with return false; in the callback, as described in the jQuery documentation.
This won't return from the calling function, but you can set a flag and test it in the outer function.
If you want an easy way to return from the outer function from inside the loop, you're better off with a simple for loop:
var array = [ 'hi', 'there' ];
for( var i = 0; i < array.length; ++i ) {
var e = array[i];
console.log(e);
return;
}
alert("I don't want to be called");
For $.get(), you should add some console.log() calls to your code and observe the order in which they are called:
function outer() {
console.log( 'in outer before $.get is called' );
$.get('http://test.com', function (data) {
console.log( 'inside $.get callback' );
});
console.log( 'in outer after $.get returns' );
}
Now what you'll notice is the order of the log messages:
in outer before $.get is called
in outer after $.get returns
inside $.get callback
See how the callback is the last thing? It's called after the outer function finishes. Therefore, there is nothing this callback can do to prevent the rest of outer from executing.
So you need to think some more about what you need to do and figure out a different way to accomplish it.
If you use $.ajax() instead of the short hand $.get(), it is possible to specify that it be executed synchronously.
$.ajax({
url: 'http://test.com',
success: function (data) {
},
async: false
});
Typically, when you want to make a decision based on the return value of a function you have it return a value and then use a conditional statement to determine what happens next.
Related
I have the following jQuery code:
function next() {
//some code here
}
function previous() {
//some code here
}
$("#next").click(function(){
next();
});
$("#previous").click(function(){
previous();
});
This works, but this doesn't:
$("#next").click(next());
$("#previous").click(previous());
Why is this happening? Is there a problem in my code, or is this just a thing with jQuery? Note: #next and #previous refer to two buttons in my html file.
The callback should be a reference to the function.
Why $("#next").click(next()); doesn't work?
func() is a function call and not a reference, which is why it is called immediately.
This,
$("#next").click(function(){
next();
});
is a preferable way in case you need to pass arguments.
Else,
$("#next").click(next) //notice just the signature without ()
This works (if the functions next and previous are defined):
$("#next").click(next);
$("#previous").click(previous);
In this case the next and previous are also callback functions, the difference between the two is,
when you call this line
$("#next").click(next()); the function is executed immediately, and you are passing the result of the next function to the eventHandler of jQuery.
and in this case
$("#next").click(next); you are passing the function next to the EventHandler of jQuery.
Btw.: in the jQuery API Documentation (https://api.jquery.com/click/) it shows all parameters for the click function and the required types it states: "...handler Type: Function( Event eventObject ) A function to execute each time the event is triggered. ..."
try like this you will get your answer,
function next() {
//some code here
}
function previous() {
//some code here
}
$("#next").click(next);
$("#previous").click(previous);
working demo jsfiddle Example
What is going on there is a little bit obscured by the syntax of anonymous functions function() { ... }. What you are doing by that is passing a function, without calling it. And I want to explain how this works:
If you have a simple function
function next() { return 5 };
It will simply return the value 5, if you call it from somewhere:
a = next(); // value of a will be 5
But what you can do too, is to pass the whole function to a. This is possible, because functions in JavaScript are actually objects:
a = next;
b = a(); // value of b will be 5
If you look at the syntax, it shows you, that putting parentheses () at the end of a function invokes it, and returns the return value. While the naked string, without parentheses hands you the function itself.
So what is a callback now, and what does click() like to get as a parameter? A callback function is a function, that gets called later; we actually hand it over, to get called later. click() would like to get such a function as parameter, and it should be clear now, that we have to pass the function without parentheses, to enable click() to call it later, instead of just passing a 5 to it.
$("#next").click(next);
So how does then the initial syntax with the anonymous function work?
function() { next(); }
actually wraps your next() into another function, which is anonymous – because it does not have a name – but is working in the same way as a named function. You can even set a variable by it:
a = function() { next(); } // a will be the anonymous function that calls next()
But calling that function a() will return nothing, because the anonymous function does not return a value (To be exactly: every function call in JavaScript is returning at least undefined, but that's a technical detail).
It can even be called immediately by putting parenthesis at the end of it:
a = function() { return next(); }() // value of a will be 5
Adding the return there will make sure, the return value of next() will be passed through the anonymous function.
This should make clear why
$("#next").click(function(){ next(); });
is working, and why
$("#next").click(next());
is not, but
$("#next").click(next);
will be a good solution.
$("#next").click(next); would work. Notice parenthesis are not required as the function/callback handler should be passed as a parameter.
I have one function using to get data from text file and alert it in another function.
var library_name; // Global Variable
function getLibraryName(){
jQuery.get('stream.txt', function(data) {
library_name = data.toString().substring(17,data.length);
});
}
function saveFeedback() {
alert(library_name);
}
When saveFeedback is called, it will alert library_name
I have been tried to put it in the same function like this
function saveFeedback() {
jQuery.get('stream.txt', function(data) {
library_name = data.toString().substring(17,data.length);
});
alert(library_name);
}
but it is still keep saying undefined in console
How to solve this out? Without using parameter because saveFeedback function has to be called from somewhere else.
The second parameter to .get is called when the get finishes. You're saying, "fetch stream.txt and when it finishes, execute this function". The following calls saveFeedback when the get is finished:
function getLibraryName(){
jQuery.get('stream.txt', function(data) {
library_name = data.toString().substring(17,data.length);
saveFeedback();
});
}
Because get is asynchronous, your interpreter will instantiate the saveFeedback function before the success function is called (although some sanity checkers like JSLint probably want you to define saveFeedback before getLibraryName.
You shoud use saveFeedback() function in $.get(data) callback, because it it async request and you do not know, when it will be completed.
You can make a hidden input field and attach data to it using $.data();
For example:
function getLibraryName(){
jQuery.get('stream.txt', function(data) {
var library_name = data.toString().substring(17,data.length);
$('#myHiddenField').data('library_name', library_name);
});
}
function saveFeedback() {
alert($('#myHiddenField').data('library_name'));
}
jQuery get initiates an asynchronous call. Asynchronous means that the result from it will return later... maybe a short time later, maybe many seconds later. After kicking off this call, the code then proceeds immediately to the next line which is your alert. This alert occurs before the return from the asynchronous call, so of course the variable is still undefined--it hasn't been set yet.
One way to solve the problem might be to tell jQuery to make the call synchronous. But this is the wrong answer because it will stop the browser from responding to user interaction while the call is occurring. Instead, you need to adjust how you think about asynchronous calls. The basic concept is that instead of having a big function that does things step by step like a normal, procedural recipe (do step 1, then step 2, then step 3), instead you have to set up two different recipes or pieces of code: one to initiate the ajax call and then exit, and one to respond to the result of the ajax call.
Now, you already have two functions. You can't combine them for the reasons I already explained. But, you can chain them, or encapsulate one inside the other:
var library_name;
function saveFeedback() {
alert(library_name);
}
function getLibraryName(){
jQuery.get('stream.txt', function(data) {
library_name = data.toString().substring(17 ,data.length);
saveFeedback(); // this chains the two functions together
});
}
or maybe even better:
var library_name;
function saveFeedback() {
alert(library_name);
}
function receiveLibraryName(data) {
library_name = data.toString().substring(17, data.length);
saveFeedback();
// additional statements here after saving
}
function getLibraryName(){
jQuery.get('stream.txt', receiveLibraryName);
}
The point is that you cannot continue in the next statement as usual within the getLibraryName function. The steps you wish to take after the ajax call must be inside of another function: either in the callback function itself, or in another function called from the callback function.
Try following code:
var library_name;
function getLibraryName(){
jQuery.get('stream.txt', function(data) {
let lbName= data.toString().substring(17,data.length);
saveFeedback(lbName);
});
}
function saveFeedback(lbName) {
library_name = lbName; //If you are using library_name in other code
alert(library_name);
}
You have to define library_name before jquery.get once. Because if the jquery.get doesnt work library_name will not be created by the time u call alert.
please add
var library_name;
as the first line of saveFeedback().
In my code, I have an array of function calls. I loop over these calls and use .apply() to call them. The problem is that if the call of the new function takes any sort of time, the loop will .apply() and call the next function before the prior function is finished. >.< Here is an example:
function someFunc(element, calls){
if(calls.length){
fn = calls[0];
calls.shift();
fn.apply(element, args);
someFunc(element, calls);
}
}
So if there was a callback on the apply function then this could work how I want it to. i.e.
function someFunc(element, calls){
if(calls.length){
fn = calls[0];
calls.shift();
fn.apply(element, args, function(){
someFunc(element, calls);
});
}
}
I also have a question about calling someFunc inside of a callback function. The functions in my calls array affect my element variable. So I want to make sure after it gets changed that it gets passed to someFunc in the callback so the next function can manipulate it as well. Sometimes I just get confused with the this context. :)
If it helps, I am using jQuery. I know how to add callbacks to jQuery methods but I don't know how to do that when I'm dealing with native JavaScript code. How can I add a callback to the .apply() method?
Make sure that every function you call returns a promise. You can then "wait" for that promise to be "resolved" before continuing with the next function in your list:
function someFunc(element, calls) {
if (calls.length) {
var fn = calls.shift();
fn.apply(element, args).done(function(el) { // what's args?
el = el || element; // default to previous element if necessary
someFunc(el, calls);
});
}
}
with each function looking something like:
function myFunc1(el) {
var def = $.Deferred();
// do something async, and "resolve" the deferred object in the async callback
...(function() {
def.resolve(el); // this "el" will get passed to the next function
});
return def.promise();
}
If the asynchronous task is an AJAX call you can just return the jqXHR result of $.ajax directly instead of creating a new deferred object.
I have a javascript function that has a callback then an anonymous function then another callback, and something has gone wrong with the scope. The parameter callbackFunc is retaining its value from the first function call and not using the new value passed in the 2nd function call.
function IsReady(callbackFunc) {
if (!IsValid()) return false;
IsConnected(function () {
if (typeof (callbackFunc) == 'function')
callbackFunc();
return true;
});
}
function IsConnected(validCallbackFunc) {
$.post("IsConnected", function (data) {
if (data.IsValid) {
if (validCallbackFunc && typeof (validCallbackFunc) == 'function')
validCallbackFunc();
}
});
}
$('#SaveButton').click(function () {
IsReady(SaveInvoice); // works
});
$('#ExportButton').click(function () {
// works only if IsConnected() is true
// otherwise SaveInvoice is called again
IsReady(ExportInvoice);
});
function SaveInvoice() {}
function ExportInvoice() {}
In some circumstances, when I click the ExportButton, the SaveInvoice function is run instead of the ExportInvoice function. I'm guessing that it's a scoping issue - that somehow the old value of callbackFunc has been retained. But I don't quite understand it due to the mix of callback + anonymous function + another callback. I didn't write this code, but I have to fix it. Is there anything I can do to clear the value of callbackFunc at the end of IsReady()?
IsReady(ExportInvoice) works if IsConnected() is true. If IsConnected() is false then the result is that SaveInvoice() gets executed when in fact nothing should happen (because it is not connected).
There is no way that the callbackFunc value could be retained between two different calls of the IsReady function.
In your code, each time a click event handler is executed, a new scope is created when IsReady is called. Each scope has it's own local parameter callbackFunc. Each scope will define its own anonymous function passed to IsConnected where resides the callbackFunc variable enclosed in a closure.
So this is not a scope problem.
To prove it, I emulated your code here: http://jsfiddle.net/pwJC7/
In your code you talk about the IsConnected return value. This function actually does not return anything. The connection status seems to be checked through an ajax call returning an XML or JSON data with an IsValid property (emulated by $_post in the fiddle).
Maybe your issue is due to this asynchronous call. But it's impossible that you experience a call to SaveInvoice function as a consequence of a click to ExportInvoice button with the JavaScript code you provided.
I have a problem returning a variable in my function, the below script works fine:
function sessionStatus(){
$(document).ready(function(){
$.getJSON(scriptRoot+"sessionStatus.php",function(status){
alert(status);
});
});
}
sessionStatus();
Bet when I try the following I get a message box with the message "undefined":
function sessionStatus(){
$(document).ready(function(){
$.getJSON(scriptRoot+"sessionStatus.php",function(status){
return status;
});
});
}
alert(sessionStatus());
This is really bugging me, I just can't seem to see what I've done wrong.
There are two things you should know:
1: the JSON thing is asynchronous, so the function call to sessionStatus could already be done when the JSON is still being fetched. The following would work:
function sessionStatus(callback){
$(document).ready(function(){
$.getJSON(scriptRoot + "sessionStatus.php", function(status){
callback(status);
});
});
}
sessionStatus(function(s){alert(s);});
or rather:
function sessionStatus(callback){
$(document).ready(function(){
$.getJSON(scriptRoot + "sessionStatus.php", callback);
});
}
sessionStatus(function(s){alert(s);});
2: even when it would be synchronous, you are only giving a return value from the inner function, so the sessionStatus would return nothing. Check out this code (not related to your JSON thing):
function do() {
var x = 0;
(function(){
x = 2;
})();
return x;
}
or:
function do() {
var x = (function(){
return 2;
})();
return x;
}
Both return 2. Hopefully this explains a bit.
Your function sessionStatus() doesn't return anything, hence the alert says undefined.
All the function does is set thing up for the AJAX call to happen once the page loads - nothing actually happens within sessionStatus() that could be returned.
The code in function(status) { ...} doesn't get run until the AJAX call to the server returns a value, and that AJAX call doesn't get sent until the page loads.
You ought to read up on what $.getJSON() and $(document).ready() actually do, because you're going to continue to get confusing behaviour until you understand them properly.
Your sessionStatus() function never returns anything. It sets a function to run later, and that function returns something, but that's not anything to do with sessionStatus()
You're returning a value when the document ready event is done. Where is your value supposed to go? The jQuery object doesn't know what to do with it.
The function sessionStatus just sets up the event listener for $(document).ready() and then returns without returning a value. That's the undefined you see.
Later when $(document).ready() fires it calls the ajax which if it succeeds returns the status, but nothing is receiving that status.
function sessionStatusCallback(status)
{
alert(status);
}
function sessionStatus(){
$(document).ready(function(){
$.getJSON(scriptRoot+"sessionStatus.php",function(status){
sessionStatusCallback(status);
});
});
}
sessionStatus();
Your function is being called asynchronously -- actually after two asynchronous calls, made via .ready() and .getJSON(). In such a case there is no available return value, instead you have to use a callback, as in the above, to process the response.
Though I should note that the function passed to getJSON in the above already is a callback. You could change that function definition to just be "sessionStatusCallback" and it would call the above callback once the JSON was ready, and you could continue to process there. Or...continue your processing in the current callback (it's a matter of style whether to use a function reference or declare the anonymous function right there in the .getJSON() call)
Functions should never be included in a jQuery(document).ready function. Separate them, so you don´t have side effects you don´t want to have. How do you want to call the session status? And witch function should get the return value?