Week 10: Asynchronous JavaScript
INFO 253A: Front-end Web Architecture
Kay Ashaolu
JavaScript Chapter 10: Asynchronous JavaScript
Synchronous vs Asynchronous JavaScript
-
Synchronous JavaScript:
- Executes code sequentially.
- Each operation waits for the previous one to complete.
-
Asynchronous JavaScript:
- Allows multiple operations to occur simultaneously.
- Non-blocking: Does not wait for an operation to finish before moving on.
- JavaScript is single-threaded but can manage asynchronous tasks via the event loop.
The Call Stack and Execution Context
-
Call Stack:
- Tracks function calls and manages execution order.
- Functions are pushed and popped off the stack.
-
Execution Context:
- Contains variables, objects, and the value of
this
.
- Contains variables, objects, and the value of
- Blocking Example:
function longTask() {
// Simulate a long task
for (let i = 0; i < 1e9; i++) {}
}
console.log('Start');
longTask();
console.log('End');
Web APIs and Asynchronous Capabilities
- Web APIs provided by the browser enable asynchronous operations.
- Include:
- DOM Events
- Timers (
setTimeout
,setInterval
) - AJAX (
XMLHttpRequest
, Fetch API) - Promises
- Extend JavaScript's capabilities beyond its core synchronous nature.
The Event Loop and Task Queue
-
Event Loop:
- Continuously checks the Call Stack and Task Queue.
- Moves tasks from the Task Queue to the Call Stack when it's empty.
-
Task Queue:
- Holds callback functions waiting to be executed.
-
Microtask Queue:
- Holds promise callbacks.
- Has higher priority than the Task Queue.
Understanding Callbacks and Callback Hell
-
Callbacks:
- Functions passed as arguments to be executed later.
- Used for handling asynchronous operations.
-
Callback Hell:
- Pyramid of doom caused by multiple nested callbacks.
- Makes code hard to read and maintain.
- Example of Callback Hell:
doFirstTask((result1) => {
doSecondTask(result1, (result2) => {
doThirdTask(result2, (result3) => {
// Continue nesting...
});
});
});
Introducing Promises
- Promises simplify asynchronous code.
-
States:
- Pending: Initial state.
- Fulfilled: Operation completed successfully.
- Rejected: Operation failed.
-
Benefits:
- Avoids callback hell.
- Enables cleaner, more readable code.
Creating and Using Promises
- Creating a Promise:
const promise = new Promise((resolve, reject) => {
// Asynchronous operation
if (success) {
resolve(result);
} else {
reject(error);
}
});
- Consuming a Promise:
promise
.then((result) => {
// Handle success
})
.catch((error) => {
// Handle error
});
Promise Chaining
- Sequentially execute asynchronous tasks.
- Example:
getUser()
.then((user) => getProfile(user.id))
.then((profile) => getPosts(profile.id))
.then((posts) => console.log(posts))
.catch((error) => console.error(error));
HTTP Requests in Web Architecture
- HTTP Requests enable communication between client and server.
-
Common HTTP Methods:
- GET: Retrieve data.
- POST: Send data.
- PUT/PATCH: Update data.
- DELETE: Remove data.
- Asynchronous requests prevent UI blocking during data fetch.
Fetch API and XMLHttpRequest (XHR)
-
Fetch API:
- Modern interface for making HTTP requests.
- Returns a Promise.
- Simplifies request code.
fetch('https://api.example.com/data')
.then((response) => response.json())
.then((data) => console.log(data));
-
XMLHttpRequest (XHR):
- Older method.
- More verbose and complex.
- Still important for understanding legacy code.
Handling Multiple Promises with Promise.all()
-
Promise.all()
waits for multiple promises to resolve. - Useful for parallel asynchronous operations.
- Example:
Promise.all([fetchData1(), fetchData2(), fetchData3()])
.then((results) => {
// results is an array of resolved values
console.log(results);
})
.catch((error) => console.error(error));
Async/Await Syntax
- Async/Await simplifies working with promises.
- Makes asynchronous code look synchronous.
- Example:
async function loadData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
Key Takeaways
- Asynchronous JavaScript is essential for modern web applications.
- Callbacks, Promises, and Async/Await help manage asynchronous operations.
- Understanding these concepts improves code readability and maintainability.
- Event Loop and Web APIs enable JavaScript to handle concurrent tasks effectively.
JavaScript Chapter 11: Fetch API and Async/Await
Asynchronous JavaScript in Web Architecture
- Understanding modern asynchronous operations in JavaScript
- Simplifying HTTP requests and responses
- Enhancing front-end web architecture with Fetch API and Async/Await
Introduction to the Fetch API
-
What is the Fetch API?
- A modern, promise-based interface for making HTTP requests
-
Key Features:
- Simplifies HTTP requests in JavaScript
- Provides a clean interface for fetching resources (e.g., JSON data, images)
- Returns Promises, enabling easier handling of asynchronous operations
- Replaces older XMLHttpRequest (XHR) methods
Basic Usage of Fetch API
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
// Handle the data
console.log(data);
})
.catch(error => {
// Handle errors
console.error('Error:', error);
});
-
Step-by-Step Explanation:
-
fetch()
initiates a network request to the provided URL - The first
.then()
processes the response, parsing it as JSON - The second
.then()
handles the parsed data -
.catch()
handles any errors that occur during the fetch or processing
-
Fetching JSON Data
- Fetch API defaults to GET requests
- Handling the response:
fetch('movies.json')
.then(response => response.json())
.then(data => {
// Use the data
console.log(data);
});
- Use
response.json()
to parse JSON responses
Fetch API Options: Method, Headers, Body
- Customizing requests with options object:
fetch('https://api.example.com/data', {
method: 'POST', // HTTP method
headers: {
'Content-Type': 'application/json', // Specifies the media type
'Authorization': 'Bearer abc123' // Example of an auth token
},
body: JSON.stringify({ title: 'New Post', body: 'This is the content.' }) // Data to send
})
.then(response => response.json())
.then(data => {
// Handle the response
console.log(data);
});
Fetch API Options: Method, Headers, Body
-
Explanation of options:
-
method
: Specifies the HTTP method (GET, POST, PUT, DELETE) -
headers
: An object containing any custom headers you want to add -
body
: The data to send with the request (for POST, PUT)
-
Handling Errors with Fetch API
-
Understanding Fetch API error handling:
- Fetch promises only reject on network failure
- HTTP errors (e.g., 404, 500) do not automatically reject the promise
- Manual error handling:
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) {
// Response status is not in the range 200-299
throw new Error('Network response was not ok ' + response.statusText);
}
return response.json();
})
.then(data => {
// Handle data
console.log(data);
})
.catch(error => {
// Handle errors
console.error('Fetch error:', error);
});
Handling Errors with Fetch API
-
Key Points:
- Check
response.ok
to determine if the request was successful - Throw an error to reject the promise if the response is not ok
- Use
.catch()
to handle any errors that occur
- Check
Introduction to Async/Await
-
What is Async/Await?
- Introduced in ES2017 (ES8)
- Allows writing asynchronous code in a synchronous style
-
Benefits:
- Simplifies complex promise chains
- Improves code readability and maintainability
- Basic Syntax:
async function fetchData() {
// Function declared with 'async' keyword
const response = await fetch('https://api.example.com/data');
// 'await' pauses the function until the promise resolves
const data = await response.json();
// Use the data
console.log(data);
}
fetchData(); // Call the async function
Introduction to Async/Await
-
Key Points:
- Functions containing
await
must be declared withasync
-
await
can only be used insideasync
functions - The
await
keyword makes JavaScript wait until the promise settles
- Functions containing
Error Handling with Async/Await and Try...Catch
-
Handling Errors in Async Functions:
- Use
try...catch
blocks to handle errors inasync
functions
- Use
- Example:
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
// Throw an error if response status is not OK
throw new Error('HTTP error! status: ' + response.status);
}
const data = await response.json();
// Use the data
console.log(data);
} catch (error) {
// Handle any errors
console.error('Error:', error);
}
}
Error Handling with Async/Await and Try...Catch
-
Key Points:
-
try
block contains code that may throw an error -
catch
block handles any errors that occur in thetry
block - Ensures errors are properly caught and handled
-
Mini-Project: Random User Generator
-
Goal:
- Create an application that fetches and displays random user data
-
API Used:
- Random User API
- Provides a JSON response with user details
-
Implementation Steps:
- Fetch user data using Fetch API and Async/Await
- Parse and extract necessary information (e.g., name, email, picture)
- Dynamically update the DOM to display user information
- Code Sample:
-
Demonstrates:
- Practical use of Fetch API and Async/Await
- Error handling in asynchronous functions
- Dynamic DOM manipulation
Mini-Project: Random User Generator
async function getRandomUser() {
try {
const response = await fetch('https://randomuser.me/api/');
const data = await response.json();
// Extract user data
const user = data.results[0];
displayUser(user);
} catch (error) {
console.error('Error fetching user:', error);
}
}
function displayUser(user) {
// Update the DOM elements with user information
document.getElementById('user-name').textContent = `${user.name.first} ${user.name.last}`;
document.getElementById('user-email').textContent = user.email;
document.getElementById('user-picture').src = user.picture.large;
}
-
Demonstrates:
- Practical use of Fetch API and Async/Await
- Error handling in asynchronous functions
- Dynamic DOM manipulation
Mini-Project: Typicode Todos - Part 1
-
Objective:
- Build a simple todo application interacting with a mock API
-
API Used:
- JSONPlaceholder
- Provides fake online REST API for testing
-
Features Implemented:
- Fetching todos from the API and displaying them
- Limiting the number of todos displayed
- Adding new todos via POST requests
- Code Sample - Fetching Todos:
-
Key Concepts:
- Using query parameters (
?_limit=5
) to limit results - Making POST requests to add data
- Handling JSON responses
- Using query parameters (
Mini-Project: Typicode Todos - Part 1
- Code Sample - Fetching Todos:
async function getTodos() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/todos?_limit=5');
const todos = await response.json();
// Display todos in the DOM
todos.forEach(todo => {
// Create DOM elements and append to the list
});
} catch (error) {
console.error('Error fetching todos:', error);
}
}
-
Key Concepts:
- Using query parameters (
?_limit=5
) to limit results - Making POST requests to add data
- Handling JSON responses
- Using query parameters (
Mini-Project: Typicode Todos - Part 1
- Code Sample - Adding a Todo:
async function addTodo() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/todos', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
title: 'New Todo',
completed: false
})
});
const newTodo = await response.json();
// Update the DOM with the new todo
} catch (error) {
console.error('Error adding todo:', error);
}
}
-
Key Concepts:
- Using query parameters (
?_limit=5
) to limit results - Making POST requests to add data
- Handling JSON responses
- Using query parameters (
Mini-Project: Typicode Todos - Part 2
-
Extended Features:
- Updating existing todos (e.g., marking as completed)
- Deleting todos
- Updating a Todo with PUT:
async function updateTodo(id, completed) {
try {
const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ completed })
});
const updatedTodo = await response.json();
// Reflect changes in the DOM
} catch (error) {
console.error('Error updating todo:', error);
}
}
Mini-Project: Typicode Todos - Part 2
- Deleting a Todo with DELETE:
async function deleteTodo(id) {
try {
await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`, {
method: 'DELETE'
});
// Remove todo from the DOM
} catch (error) {
console.error('Error deleting todo:', error);
}
}
-
Key Concepts:
- Using dynamic URLs with template literals
- Performing PUT and DELETE requests
- Understanding HTTP methods for RESTful APIs
Fetch API in Web Architecture
-
Role in Modern Web Applications:
- Central to client-server communication
- Facilitates fetching and sending data asynchronously
-
Advantages over Older Methods:
- Cleaner syntax than XMLHttpRequest (XHR)
- Returns Promises, enabling better error handling and chaining
-
Integration with Other Technologies:
- Works seamlessly with modern JavaScript frameworks (e.g., React, Vue)
- Can be used with service workers for caching and offline support
Best Practices with Fetch API and Async/Await
-
Error Handling:
- Always use
try...catch
blocks in async functions - Check
response.ok
and handle HTTP errors
- Always use
-
Code Organization:
- Keep fetch calls and DOM manipulation separate
- Use helper functions for repeated tasks (e.g., error handling)
Conclusion and Further Resources
-
Key Takeaways:
- Fetch API and Async/Await simplify asynchronous programming
- Essential tools for modern front-end development
- Enable cleaner, more maintainable code
Week 10 - Async JavaScript / Fetch API
By kayashaolu
Week 10 - Async JavaScript / Fetch API
Course Website: https://www.ischool.berkeley.edu/courses/info/253a
- 26