Lecture Notes Of Day 15
Asynchronous JavaScript
Objective:
The
objective of this class is to help students understand how JavaScript handles
asynchronous operations, allowing for non-blocking execution of tasks. This
will include learning about callbacks, promises, and the more
modern async/await syntax for managing asynchronous code.
What is
Asynchronous JavaScript?
In
JavaScript, asynchronous programming is a method of handling tasks that
take time (like data fetching, file reading, or network requests) without
blocking the rest of the code from executing. Asynchronous operations allow
JavaScript to execute other code while waiting for a task to complete.
Synchronous
code, on the
other hand, executes line by line. This can be inefficient, especially if there
are tasks that take a long time to complete.
Asynchronous
operations are commonly used in situations like:
- Making
network requests (e.g., loading data from an API)
- Reading
from or writing to a file
- Handling
user input events
1.
Callbacks:
A callback
is a function that is passed as an argument to another function, and it is
executed once the asynchronous operation is complete. Callbacks allow us to
handle the result of an asynchronous operation.
Example
of a Callback:
function fetchData(callback) {
setTimeout(() => {
console.log("Data fetched successfully.");
callback("Data is here!");
}, 2000);
}
function handleData(data) {
console.log(data);
}
fetchData(handleData);
Explanation:
- fetchData
simulates an asynchronous operation (e.g., fetching data from a server).
- The
setTimeout function delays the execution of the code for 2 seconds to
simulate an async task.
- Once
the data is fetched, the handleData function is called via the callback to
process the result.
Callback Problem (Callback Hell):
One of the main issues with callbacks is that they
can lead to deeply nested functions, which can make the code difficult to read
and maintain. This is often referred to as "callback hell."
Example of Callback Hell:
fetchData(function(data) {
processData(data, function(result) {
saveData(result, function(response) {
console.log('Data saved: ', response);
});
});
});
As you
can see, callbacks can become hard to manage when they are nested. This is
where promises and async/await come in handy.
2.
Promises:
A promise
is a modern alternative to callbacks. A promise represents a value that may not
be available yet but will be resolved in the future. Promises can be in one of
three states:
1.Pending: The
initial state; the promise is still being processed.
2.Resolved
(Fulfilled): The asynchronous operation has completed successfully.
3. Rejected: The
asynchronous operation has failed.
Promise
Syntax:
let promise = new Promise(function(resolve, reject) {
//
Simulating a task with setTimeout
setTimeout(() => {
let
success = true; // You can change this to false to simulate an error
if
(success) {
resolve("Data fetched successfully!");
} else {
reject("Error fetching data.");
}
}, 2000);
});
promise
.then(result => {
console.log(result); // Success: 'Data fetched successfully!'
})
.catch(error => {
console.log(error); // Failure: 'Error fetching data.'
});
Explanation:
- A
Promise is created, and inside its executor function, an asynchronous
operation (simulated by setTimeout) is performed.
- If
the operation is successful, resolve() is called, passing the result.
- If
an error occurs, reject() is called with the error message.
- The
then() method is used to handle the resolved value, and catch() is used to
handle any errors.
Promise
Chaining: Promises allow you to chain multiple .then() calls for handling
sequential asynchronous operations.
fetchData()
.then(response => {
console.log(response);
return
processData(response);
})
.then(result => {
console.log(result);
return
saveData(result);
})
.then(finalResponse => {
console.log(finalResponse);
})
.catch(error => {
console.log("Error:", error);
});
Chaining
.then() calls makes the code cleaner and more readable compared to nested
callbacks.
3.
Async/Await:
async and
await are keywords introduced in ES8 (ECMAScript 2017) that simplify working
with promises. async is used to declare a function that will return a promise,
and await is used to pause the execution of the code until the promise is
resolved.
Async
Function:
An async
function automatically returns a promise and allows you to use await within
it.
async function fetchData() {
let data =
await fetch('https://jsonplaceholder.typicode.com/posts');
let json =
await data.json();
return
json;
}
fetchData().then(data => console.log(data));
Explanation:
- fetchData
is an async function that fetches data from an API.
- Inside
the function, we use await to wait for the promise returned by fetch to
resolve.
- The
result is then processed using await for parsing the JSON data.
Error
Handling with Async/Await:
Handling
errors in async/await is easy using try...catch blocks:
async function fetchData() {
try {
let
response = await fetch('https://jsonplaceholder.typicode.com/posts');
if
(!response.ok) {
throw
new Error('Network response was not ok');
}
let data
= await response.json();
console.log(data);
} catch
(error) {
console.error('There was a problem with the fetch operation:', error);
}
}
Explanation:
- If
an error occurs (e.g., network issue or invalid response), it will be
caught in the catch block and logged.
Async/Await
vs Promises:
- async/await
provides a more synchronous way of handling asynchronous code, making it
easier to read and write.
- Promises,
while powerful, often require chaining .then() and .catch(), which can
become cumbersome in complex scenarios.
Key
Points to Remember:
- Callbacks are
functions passed to other functions and executed later, but they can
result in complex nested structures (callback hell).
- Promises
represent the eventual completion (or failure) of an asynchronous
operation and provide a cleaner syntax using .then() and .catch() for
handling success and failure.
- Async/Await
simplifies promises, allowing asynchronous code to be written in a more
readable, synchronous-like manner.
Practice
Exercise:
Task 1: Write a
function fetchDataFromAPI() that simulates fetching data from an API. It should
return a promise that resolves with a message after 2 seconds. Use async/await
to call this function and log the result.
Task 2: Using
promises, write a program that performs the following tasks sequentially:
1. Fetch
data from one API (simulate this using setTimeout).
2. Process
the data (simulate this by adding some text to the fetched data).
3. Log the
final result.
