JavaScript is single-threaded. Async programming allows JS to handle long running tasks without blocking the main execution thread, ensuring a responsive user experience. ## Callbacks Callbacks are functions passed as arguments to other functions and are executed after the completion of a task. Callbacks were the original way to handle async operations in JavaScript but are now considered outdated, resulting in "callback hell." ```javascript myDiv.addEventListener("click", function() { // execute callback }); ``` ## Promises A promise represents a value that may be available now, in the future, or never. Promises provide a cleaner and more robust way to handle async operations. ##### Promise states - Pending: Initial state, neither fulfilled or rejected. - Fulfilled: Operation completed successfully. - Rejected: Operation failed. ```javascript function fetchUser(userId) { return new Promise((resolve, reject) => { setTimeout(() => { if (isValid(userId)) { const fetchedUser = ...; resolve(fetchedUser); } else { reject(new Error('User not found.')); } }, 1000); }); }; ``` ```javascript fetchUser('123') .then((user) => { /* handle result */ }) .catch((error) => { /* handle error */ }) .finally(res => { /* called regardless of success or failure */ }); ``` ##### Handling multiple promises `Promise.all` allows you to wait for multiple promises to resolve, returning a single promise. ```javascript Promise.all([promise1, promise2]) .then((result) => { /* [result 1, result 2] */ }) ``` `Promise.race` returns a promise that resolves or ejects as soon as one of the promises in the array resolves or rejects. ```javascript Promise.race([promise1, promise2]) .then((result) => { /* Result of first resolved promise */ }) ``` ## async/await `async`/`await` are syntactic sugar over promises, making async code read more like synchronous code. (Really. The async function actually returns a promise. Returning from an async function resolves the promise. Throwing an error rejects the promise.) `async` declares an asynchronous function that returns a promise. `await` pauses the execution of an async function until the promise is resolved or rejected. See the difference below, both code blocks do the exact same thing: ```javascript // fetch using promise syntax (without async/await) function getPersonsInfo(name) { return server.getPeople().then(people => { return people.find(person => { return person.name === name }); }); } ``` ```javascript // fetch using async/await syntax async function getPersonsInfo(name) { const people = await server.getPeople(); return people.find(person => { return person.name === name }); } ``` #### Error handling Use try...catch blocks to handle errors in async/await. ```javascript async function asyncFetch(url) { try { const response = await fetch(url); if (!response.ok) { throw new Error('Network response was not ok'); } const data = await response.json(); return data; } catch (err) { // handle error } } ``` Since `async` functions return a promise, you can handle errors at their call site by appending `.catch()`, like any other promise. ```javascript asyncFetch(url) .then(data => { /* handle data */ }) .catch(err =>. { /* handle error */ }); ```