I'm converting synchronous ajax calls to asynchronous because synchronous calls are no longer supported in most browsers. Since my code is filled with synchronous (and dynamic) ajax calls (to client-side functions) where the code absolutely cannot continue until the call is complete, I'm hoping I can do something like replacing synchronousAjaxFunction();
with waitForCompletion(asynchronousAjaxFunction());
but haven't found any solutions that allow this to work without turning my code inside-out. Any help would be much appreciated.
First of, make sure to remove any async: false,
from your jQuery scripts, since synchronous XHR can hang the main thread leading to unresponsive applications.
Then, if you're using a jQuery version lower then 3.0 create a Promise wrapper for $.ajax
like:
// For jQuery v. < 3.0. jQuery v. 3.0+ implement already A* Promises.
// Use $ajaxP instead of $.ajax
const $ajaxP = (opts) => new Promise((res, rej) => $.ajax(opts).done(res).fail(rej));
One way to do it would be by using await as top-level await (without needing to define an async
wrapper function) inside a module - but such is considered bad practice since it's hard to reason about your program flow. If not used correctly could lead to undesired results. Without properly understanding all the implications you should not per-se rewrite AJAX calls to make them "synchronous"-but-good.
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
<script type="module">
console.log("Some code before...");
const data = await $.ajax({url: "https://jsonplaceholder.typicode.com/todos/1"});
console.log(data);
console.log("Some code that uses data...");
</script>
What you can do instead is to slightly rewrite your code to make it look and behave like it's synchronous, by using Promises and wrapping the crucial synchronous code into async
functions.
Here's a starting synchronous sample code we need to rewrite into a better, Promise-based approach:
// !!! WARNING !!!
// This a bad example that we need to rewrite
// from async:false AJAX call to a better, Promises based approach!
let data;
console.log("Some code before...");
$.ajax({
url: "https://jsonplaceholder.typicode.com/todos/1",
async: false,
}).done((response) => {
data = response;
}).fail(console.error);
console.log(data);
console.log("Some code that uses data...");
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
Now you can use $ajaxP
in a similar fashion, just make sure to use async
and await
in your functions:
const someFunction = async () => { // Make your function async
console.log("Some code before...");
const data = await $.ajax({
url: "https://jsonplaceholder.typicode.com/todos/1"
});
console.log(data);
console.log("Some code that uses data...");
};
someFunction();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
For better error handling use a try/catch
block:
const someFunction = async () => { // Make your function async
let data;
console.log("Some code before...");
try {
data = await $.ajax({
url: "https://jsonplaceholder.typicode.com/todos/1"
});
} catch (err) {
console.error(err);
// Do something about the error and data
}
console.log(data);
console.log("Some code that uses data...");
};
someFunction();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
If needed you can also use $ajaxP
with Promise.prototype.then
$.ajax({url: "https://jsonplaceholder.typicode.com/todos/1"})
.then((data) => {
console.log("Some code before...");
console.log(data)
console.log("Some code that uses data...");
})
.catch(console.error);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
or just the default jQuery way:
$.ajax({url: "https://jsonplaceholder.typicode.com/todos/1"})
.done((data) => {
console.log("Some code before...");
console.log(data)
console.log("Some code that uses data...");
})
.fail(console.error);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
If you have time and plan to get rid of jQuery, as a heads-up you can use JavaScript's Fetch instead:
fetch("https://jsonplaceholder.typicode.com/todos/1", {/* options here */})
.then(res => res.json())
.then((data) => {
console.log("Some code before...");
console.log(data)
console.log("Some code that uses data...");
})
.catch(console.error);
or using async/await:
console.log("Some code before");
(async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/todos/1", {/* options here */});
const data = await res.json();
console.log(data);
console.log("Some code after that uses data");
})();
console.log("Some code after that does NOT uses data");
or as already mentioned, inside a Module, with top-level await:
<script type="module">
console.log("Some code before");
const res = await fetch("https://jsonplaceholder.typicode.com/todos/1", {/* options here */});
const data = await res.json();
console.log(data);
console.log("Some code after that uses data");
</script>