This lesson traces the evolution of asynchronous JavaScript from the Event Loop and basic delegation, through Promises with .then()/.catch(), to the modern async/await syntax ā including error handling with try/catch and running multiple requests in parallel with Promise.all().
1JS Asynchronicity | JavaScript Tutorial - In-Depth Guide Part 1
JavaScript is single-threaded, but it handles multiple tasks at once using Asynchronicity. Let's see how!
// The Async PowerhouseJS Async Powerhouse
2JS Asynchronicity | JavaScript Tutorial - In-Depth Guide Part 2
Synchronous code blocks. Asynchronous code allows the program to keep running while waiting for a task (like a timer).
console.log('Start');
setTimeout(() => console.log('Timer done'), 2000);
console.log('End');Delegation
Main Thread
Web API
3JS Asynchronicity | JavaScript Tutorial - In-Depth Guide Part 3
A Promise is an object representing the eventual completion (or failure) of an asynchronous operation.
const myPromise = new Promise((resolve, reject) => {
const success = true;
if (success) resolve('Data found!');
else reject('Error!');
});The Promise
ā¬ļø
Fulfilled ā OR Rejected ā
4JS Asynchronicity | JavaScript Tutorial - In-Depth Guide Part 4
Use '.then()' to handle the success and '.catch()' to handle errors. Promises can be chained together.
myPromise
.then(data => console.log(data))
.catch(err => console.error(err));Promise Chaining
5JS Asynchronicity | JavaScript Tutorial - In-Depth Guide Part 5
Async/Await is the modern, cleaner way to write asynchronous code. It looks like synchronous code but is non-blocking.
async function loadData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.log('Oops!', error);
}
}Async / Await
.then()
await
6JS Asynchronicity | JavaScript Tutorial - In-Depth Guide Part 6
Error handling with try/catch is essential when using async/await to prevent your app from crashing.
try {
await riskyTask();
} catch (e) {
console.log('Caught!', e);
}Safety Net
catch { š }
7JS Asynchronicity | JavaScript Tutorial - In-Depth Guide Part 7
Promise.all() allows you to run multiple promises in parallel and wait for all of them to finish.
const results = await Promise.all([
fetch('/users'),
fetch('/posts')
]);Parallel Fetching
8JS Asynchronicity | JavaScript Tutorial - In-Depth Guide Part 8
Summary: Callbacks led to Promises, and Promises led to Async/Await. You now have the full toolkit!
<h1>Async: Secured</h1>Evolution
ā¬ļø
Promises
ā¬ļø
Async / Await
9JS Asynchronicity | JavaScript Tutorial - In-Depth Guide Part 9
Asynchronicity mastered! You are now ready to build high-performance, responsive applications.
<h1>Performance: Optimized</h1>Performance: Optimized
10Step-by-Step Breakdown
JavaScript is single-threaded, but it handles multiple tasks at once using Asynchronicity. Let's see how!
Synchronous code blocks. Asynchronous code allows the program to keep running while waiting for a task (like a timer).
A Promise is an object representing the eventual completion (or failure) of an asynchronous operation.
Checkpoint: What are the three possible states of a JavaScript Promise?
- āStart, Stop, Error
- āPending, Fulfilled, Rejected
- āWaiting, Done, Failed
Use '.then()' to handle the success and '.catch()' to handle errors. Promises can be chained together.
Async/Await is the modern, cleaner way to write asynchronous code. It looks like synchronous code but is non-blocking.
Checkpoint: Which keyword is used to wait for a Promise to resolve inside an async function?
- āwait
- āawait
- āpause
Error handling with try/catch is essential when using async/await to prevent your app from crashing.
Promise.all() allows you to run multiple promises in parallel and wait for all of them to finish.
Summary: Callbacks led to Promises, and Promises led to Async/Await. You now have the full toolkit!
Final Challenge: If one promise in Promise.all() fails, what happens to the whole operation?
- āThe others continue normally
- āThe whole operation immediately rejects
Asynchronicity mastered! You are now ready to build high-performance, responsive applications.
Level Up š
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Announce Async Results with aria-live Instead of Relying Only on Visual Feedback
When a Promise resolves or an async/await call completes and updates the page (a success message, new data, an error), that update should sit inside an `aria-live="polite"` (or `"assertive"` for errors) region so screen reader users are notified, not just sighted users watching the screen change.
SEO Implications
- 1
Content Populated by Unresolved Promises at Crawl Time Can Be Missing from the Indexed Page
If critical content depends on a Promise chain or async/await call that resolves after the initial page load, a crawler that indexes before that resolution completes may see an empty or incomplete page. Resolving essential data server-side before responding avoids this timing gap.
Best Practices
Prefer async/await Over Raw Promise Chains for Readability
A chain of several .then() calls, especially with nested logic or multiple .catch() handlers, becomes hard to read and debug. async/await expresses the same logic as flat, sequential statements with a single try/catch, making the control flow much easier to follow.
Run Independent Promises with Promise.all() Instead of Sequential Awaits
Awaiting one request and then starting the next means the second request doesn't even begin until the first finishes, needlessly adding their durations together. When requests don't depend on each other, start them together and await the group with Promise.all() so they run concurrently.
Frequent Bugs
Forgetting a .catch() at the end of a Promise chain, or a try/catch around an awaited call, letting rejected promises go unhandled.
An unhandled Promise rejection can silently fail or, in Node.js, crash the process. Always terminate a .then() chain with .catch(), or wrap awaited calls in try/catch, so every possible rejection has an explicit handler.
Awaiting several independent requests one after another instead of running them in parallel with Promise.all().
Sequential awaits add each request's duration together ā three 1-second requests take 3 seconds combined. Starting them together and awaiting the group with Promise.all() reduces the total wait to roughly the duration of the single slowest request.
Real-World Examples
Migrating a Nested Promise Chain to async/await
A data-loading function had grown into several nested .then() callbacks that were becoming hard to read and debug, so it was rewritten using async/await with a single try/catch block for all error handling.
// Before: nested .then() chain
fetchUser().then(user => {
return fetchPosts(user.id).then(posts => {
console.log(posts);
});
}).catch(err => console.error(err));
// After: flat async/await
async function loadUserPosts() {
try {
const user = await fetchUser();
const posts = await fetchPosts(user.id);
console.log(posts);
} catch (err) {
console.error(err);
}
}