Самый простой способ избежать глобализаций - это вообще не работать в глобальном масштабе. Способ сделать это - создать IIFE (выражение для немедленного вызова функции), которое является анонимной функцией, находящейся в глобальной области видимости, но, поскольку она анонимна, у нее нет шансов столкнуться с другой функцией с одно и то же имя Он немедленно вызывается для выполнения кода внутри него (например, операция типа init
). Затем внутри этой функции вы делаете то, что обычно делаете. Здесь вы можете создать переменную в области IIFE (не глобальной).
// 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
С другой стороны, ваш код не будет работать даже при этом, потому что вы настраиваете обработчик события click
кнопки до того, как вызов AJAX сможет завершиться. Тот факт, что этот код идет после асинхронного кода, не означает, что он будет выполняться после асинхронного кода. Весь смысл асинхронной операции в том, что мы не знаем, когда она завершится, и, поскольку JavaScript работает в однопоточной среде, в то время как асинхронная операция идет своим делом, текущая функция выполняется до конца. Только после того, как текущий поток простаивает, будут вызваны результаты асинхронной операции (обратный вызов успеха). Если обработчик событий полагается на результат вызова AJAX, вам необходимо настроить обработчик событий внутри обратного вызова «success», что в любом случае делает глобальную бессмысленной потребность. Вы все равно должны поместить весь свой код в IIFE, так как это лучшая практика.
Вот пример, который показывает другой тип асинхронной операции (таймер), который все еще работает так же, как ваш AJAX-вызов, который показывает проблему:
// 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);
Так что на самом деле вам даже не нужен глобал, вы просто переместите код привязки события в обратный вызов успеха. С точки зрения пользовательского интерфейса вы можете захотеть, чтобы кнопка начала отключаться, а затем при успешном обратном вызове включите ее, чтобы ее нельзя было нажимать, пока не установлен обработчик click
.
// 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
Вот рабочий пример всего этого:
(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>