Reputation: 83
If I have a function with an ajax call, that gets some value from the database and then I need to send that value back to the php script and so on, how should I declare this variable ?
function call(id){
$.ajax({
. . .
data: {id: id},
success: function(res){
window.id = res.id
}
});
}
//first call the function with a value
call(10);
//then call it with the returned value every time the btn gets clicked
$('#btn').click(function(){
call(window.id);
})
From my understanding is bad practice to use global variables like this because the global scope is already crowded, and it takes longer for the script to look up the variable. Can I define this value in a way that let's me send it back without being global ?
Upvotes: 0
Views: 76
Reputation: 65825
The simplest way to avoid globals is to not work in the global scope in the first place. The way to do that is to create an IIFE (Immediately Invoked Function Expression), which is an anonymous function that is in the global scope, but since it's anonymous, it has no chance of colliding with another function with the same name. It's immediately invoked so that the code within it can execute (like an init
kind of operation). Then, inside that function you do whatever you would normally do. This is where you would then create your IIFE scoped (not global scoped) variable.
// IIFE wrapper
(function(){
let mainID = null; // IIFE scoped variable
function call(id){
$.ajax({
. . .
data: {id: id},
success: function(res){
mainID = res.id; // Set the IIFE scoped variable to the AJAX result
}
});
}
// Set up the event
$('#btn').click(function(){
all(res.id); // <-- And, you can use it anywhere in the IIFE
});
call(10); // first call the function with a value
})(); // <-- The extra set of parenthesis is what invokes the expression
On a different note, your code won't work even with this in place because you are setting up the button's click
event handler before the AJAX call has had a chance to complete. Just because that code comes after the async code, doesn't mean it will execute after the async code. The whole point of an async operation is that we don't know when it will complete and since JavaScript runs in a single-threaded environment, while the async operation is going about its business, the current function runs to completion. Only after the current thread is idle, will the results of the async operation (the success callback) be invoked. If the event handler relies on the result of the AJAX call, you'd need to set up the event handler inside the "success" callback, thus making the need for the global pointless anyway. You should still put all your code in an IIFE though as that is a best-practice.
Here's an example that shows a different type of async operation (a timer), which still operates like your AJAX call does that shows the problem:
// Global variable
var result = null;
// Start an async operation that will execute the callback
// after three seconds:
setTimeout(function(){
result = "complete"
console.log("Async operation " + result);
}, 3000);
// This function will run before the async operation
// completes, so the use of the variable that is set
// by the async operation will fail
console.log("Local operation " + result);
So actually, you don't even need a global in the first place, you would just move the event binding code into the success callback. From a UI perspective, you may want the button to start out disabled and then in the success callback, enable it so that it can't be clicked until the click
handler is set.
// IIFE wrapper
(function(){
function call(id){
$.ajax({
. . .
data: {id: id},
success: function(res){
// Set up the event now that the AJAX call is complete
$('#btn').click(function(){
call(res.id);
});
}
});
}
call(10); // first call the function with a value
})(); // <-- The extra set of parenthesis is what invokes the expression
Here's a working example of the whole thing:
(function(){
// IIFE scoped variables
let btn = document.querySelector("button");
let random = null;
function async(){
// Async operation complete!
// Generate a random number that simulates the
// DB response that would now be available
random = Math.floor(Math.random() * 11);
// Enable the button:
btn.removeAttribute("disabled");
// Set up the button's click event handler
btn.onclick = function(){
console.log("Button clicked. ID = " + random);
async();
};
}
// Start an async operation that will execute the callback
// after three seconds:
setTimeout(async, 3000);
})();
<button type="button" disabled>Click Me</button>
Upvotes: 4
Reputation: 18413
As the id
is semantically tied to the #btn
, you might store the returned id
value as the #btn
property:
function call(id){
$.ajax({
. . .
data: {id: id},
success: function(res){
$('#btn').prop('response_id', res.id);
}
});
}
//first call the function with a value
call(10);
//then call it with the returned value every time the btn gets clicked
$('#btn').click(function(){
call(this.prop('response_id'));
})
Upvotes: 0
Reputation: 5769
Just replace window.id
with call.id
(that is, store it as a property of your function):
function call(id){
$.ajax({
. . .
data: {id: id},
success: function(res){
call.id = res.id
}
});
}
//first call the function with a value
call(10);
//then call it with the returned value every time the btn gets clicke
$('#btn').click(function(){
call(call.id);
})
Upvotes: -3