Asynchronous JavaScript ⏳
JavaScript runs single-threaded, so async patterns keep apps responsive.
1) Callbacks 📞
readFile("config.json", (error, data) => {
if (error) return console.error(error);
console.log(data);
});- Pass a function to run when work completes.
- Beware “callback hell” (deep nesting) and remember to handle errors first.
2) Promises 🤝
fetch("/api/todos")
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error))
.finally(() => console.log("done"));- Promise states: pending → fulfilled/rejected.
- Chain
then/catchto compose asynchronous steps.
3) async/await ✨
async function loadTodos() {
try {
const response = await fetch("/api/todos");
const data = await response.json();
return data;
} catch (error) {
console.error(error);
}
}- Syntactic sugar over Promises; use
try...catchfor errors. - Run in parallel with
await Promise.all([...]).
4) Event Loop 🌀
- JS executes stack frames; asynchronous callbacks queue up in the task or microtask queue.
- Microtasks (Promises) run before the next rendering tick; macrotasks (timers, DOM events) run afterwards.
Order Example
console.log("start");
setTimeout(() => console.log("timeout"));
Promise.resolve().then(() => console.log("microtask"));
console.log("end");
// start, end, microtask, timeoutKey Takeaways ✅
- Callbacks were first, Promises add structure, async/await keeps code readable.
- Always handle errors and consider concurrency with
Promise.all/Promise.race. - Understanding the event loop explains why microtasks run before timers.
Recap 🔄
Async JavaScript keeps the UI smooth: wrap work in Promises, await results cleanly, and leverage knowledge of the event loop to avoid surprises.
Last updated on