Asynchronous JavaScript
JavaScript runs on a single thread, but it can handle asynchronous operations such as timers, HTTP requests, or events. This allows applications to remain responsive and not block the user interface.
Example: Synchronous vs. Asynchronous Code
function runSync() {
document.getElementById("outputIntro").textContent = "";
document.getElementById("outputIntro").textContent += "๐น Starting synchronous execution\n";
for (let i = 0; i < 3; i++) {
document.getElementById("outputIntro").textContent += `๐ธ Step ${i + 1}\n`;
}
document.getElementById("outputIntro").textContent += "โ
Synchronous end\n";
}
function runAsync() {
document.getElementById("outputIntro").textContent = "";
document.getElementById("outputIntro").textContent += "๐น Starting asynchronous execution\n";
for (let i = 0; i < 3; i++) {
setTimeout(() => {
document.getElementById("outputIntro").textContent += `๐ธ Step ${i + 1}\n`;
if (i === 2) {
document.getElementById("outputIntro").textContent += "โ
Asynchronous end\n";
}
}, 1000 * (i + 1));
}
}
setTimeout and setInterval
These two functions allow delayed or repetitive execution of code without blocking the rest of the application.
// setTimeout: executes a function once after a delay
function startTimeout() {
document.getElementById("timeoutOutput").textContent = "โณ Waiting 3 seconds...";
setTimeout(() => {
document.getElementById("timeoutOutput").textContent = "โ
Timeout completed!";
}, 3000);
}
// setInterval: executes a function repeatedly every X milliseconds
let intervalId;
let counter = 0;
function startInterval() {
counter = 0;
document.getElementById("intervalOutput").textContent = "๐ Started...";
intervalId = setInterval(() => {
counter++;
document.getElementById("intervalOutput").textContent = `๐ Interval: ${counter} seconds`;
if (counter === 5) {
stopInterval();
document.getElementById("intervalOutput").textContent += "\nโน๏ธ Interval stopped automatically.";
}
}, 1000);
}
function stopInterval() {
clearInterval(intervalId);
document.getElementById("intervalOutput").textContent += "\n๐ Interval stopped manually.";
}
setTimeout
setInterval
Callbacks and "Callback Hell"
A callback is a function passed as an argument, which will be called later โ usually after an asynchronous operation. However, when multiple callbacks are nested, the code becomes hard to read and maintain, known as "Callback Hell".
Simple Callback Example
function showMessage(text, callback) {
document.getElementById("callbackOutput1").textContent = "โณ Waiting...";
setTimeout(() => {
document.getElementById("callbackOutput1").textContent = text;
if (callback) callback();
}, 2000);
}
function runCallback() {
showMessage("โ
Message displayed after 2 seconds", () => {
console.log("Callback executed!");
});
"Callback Hell" Example
// Example of "Callback Hell"
function runCallbackHell() {
const out = document.getElementById("callbackOutput2");
out.textContent = "๐น Starting execution...\n";
setTimeout(() => {
out.textContent += "๐ธ Step 1\n";
setTimeout(() => {
out.textContent += "๐ธ Step 2\n";
setTimeout(() => {
out.textContent += "๐ธ Step 3\n";
setTimeout(() => {
out.textContent += "โ
Done!\n";
}, 1000);
}, 1000);
}, 1000);
}, 1000);
}
Promises and .then/.catch
A promise is an object representing an asynchronous operation that can be resolved or
rejected. We use .then() for success and .catch() for errors.
Example: Promise that resolves
function runPromise() {
document.getElementById("promiseOutput1").textContent = "โณ Waiting...";
new Promise((resolve, reject) => {
setTimeout(() => resolve("โ
Promise resolved!"), 2000);
})
.then(result => {
document.getElementById("promiseOutput1").textContent = result;
})
.catch(err => {
document.getElementById("promiseOutput1").textContent = "โ Error: " + err;
});
}
Example: Promise that fails
function runPromiseWithError() {
document.getElementById("promiseOutput2").textContent = "โณ Waiting...";
new Promise((resolve, reject) => {
setTimeout(() => reject("๐ฅ Something went wrong..."), 2000);
})
.then(result => {
document.getElementById("promiseOutput2").textContent = result;
})
.catch(err => {
document.getElementById("promiseOutput2").textContent = "โ Caught error: " + err;
});
}
Async/Await Syntax
async and await are modern syntax for working with promises. They make asynchronous
code look like synchronous code, without blocking execution.
Example: Simulating an asynchronous operation
async function runAsync() {
document.getElementById("asyncOutput1").textContent = "โณ Waiting...";
const result = await new Promise(resolve => {
setTimeout(() => resolve("โ
Operation completed!"), 2000);
});
document.getElementById("asyncOutput1").textContent = result;
}
Example: Handling an error elegantly
async function runAsyncWithError() {
document.getElementById("asyncOutput2").textContent = "โณ Trying...";
try {
const result = await new Promise((_, reject) => {
setTimeout(() => reject("๐ฅ Simulated error!"), 2000);
});
document.getElementById("asyncOutput2").textContent = result;
} catch (err) {
document.getElementById("asyncOutput2").textContent = "โ Caught gracefully: " + err;
}
}
Fetch API and HTTP Requests
fetch() is a native JavaScript function that allows sending HTTP requests to servers. It is
asynchronous and returns a promise.
Example: Fetching JSON Data
async function fetchPost() {
document.getElementById("fetchOutput1").textContent = "โณ Loading...";
try {
const response = await fetch("https://jsonplaceholder.typicode.com/posts/1");
const data = await response.json();
document.getElementById("fetchOutput1").textContent = JSON.stringify(data, null, 2);
} catch (err) {
document.getElementById("fetchOutput1").textContent = "โ Error: " + err.message;
}
}
Example: Sending Data (POST)
async function sendPost() {
document.getElementById("fetchOutput2").textContent = "โณ Sending...";
try {
const response = await fetch("https://jsonplaceholder.typicode.com/posts", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
title: "Hello John!",
body: "This is a message sent via Fetch.",
userId: 7
})
});
const data = await response.json();
document.getElementById("fetchOutput2").textContent = JSON.stringify(data, null, 2);
} catch (err) {
document.getElementById("fetchOutput2").textContent = "โ Error: " + err.message;
}
}
Error Handling in Asynchronous Code
In asynchronous JavaScript, errors can occur during fetch requests, promises, or delayed operations. We can
handle them using .catch() or try/catch inside async functions.
Example: Error caught with .catch()
function fetchWithCatch() {
document.getElementById("errorOutput1").textContent = "โณ Attempting...";
fetch("https://invalid-url.typicode.com/")
.then(response => response.json())
.then(data => {
document.getElementById("errorOutput1").textContent = JSON.stringify(data);
})
.catch(err => {
document.getElementById("errorOutput1").textContent = "โ Error caught with .catch(): " + err.message;
});
}
Example: Error caught with try/catch
async function fetchWithTryCatch() {
document.getElementById("errorOutput2").textContent = "โณ Attempting...";
try {
const response = await fetch("https://invalid-url.typicode.com/");
const data = await response.json();
document.getElementById("errorOutput2").textContent = JSON.stringify(data);
} catch (err) {
document.getElementById("errorOutput2").textContent = "โ Error caught with try/catch: " + err.message;
}
}