WEB Programming
JavaScript
04-Promise

Promise

Callback Review

  • Callbacks are a traditional way to manage asynchronous code but can lead to callback hell with nested functions.
function fetchData(callback) {
    setTimeout(() => {
        callback("Data received");
    }, 1000);
}
 
fetchData((data) => {
    console.log(data);
});

The fetchData function simulates fetching data with a delay and then calls the provided callback function with the data. As more asynchronous operations are nested, this can lead to "callback hell."


Introducing Promises

  • Promises provide a cleaner and more structured way to handle asynchronous operations by chaining then and catch methods.
const fetchData = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("Data received");
    }, 1000);
});
 
fetchData.then((data) => {
    console.log(data);
}).catch((error) => {
    console.error(error);
});

The example shows a Promise that resolves after a delay. The .then method handles the resolved value, and .catch handles any errors. This approach avoids deeply nested callbacks and makes the code more readable.


Chaining Promises

  • Chaining promises ensures that asynchronous tasks are executed in the correct order and errors are handled gracefully.
const fetchData = () => new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("Data received");
    }, 1000);
});
 
fetchData()
    .then((data) => {
        console.log(data);
        return "Next task";
    })
    .then((nextTask) => {
        console.log(nextTask);
    })
    .catch((error) => {
        console.error(error);
    });

This example shows how promises can be chained to execute tasks sequentially. The first .then handles the initial promise, and the second .then handles the subsequent task. Errors in any part of the chain are caught by .catch.


Error Handling

  • Error handling is essential in asynchronous code to prevent unexpected behavior and ensure proper flow of execution.
  • Promises allow for centralized error handling using the .catch method at the end of the chain.
const fetchData = () => new Promise((resolve, reject) => {
    setTimeout(() => {
        reject("Error occurred");
    }, 1000);
});
 
fetchData()
    .then((data) => {
        console.log(data);
    })
    .catch((error) => {
        console.error(error);
    });

The .catch method ensures that any errors are handled properly, preventing the application from crashing unexpectedly.


Async/Await

  • Async/await simplifies asynchronous programming by allowing developers to write asynchronous code in a synchronous style, making it easier to read and maintain.
const fetchData = () => new Promise((resolve) => {
    setTimeout(() => {
        resolve("Data received");
    }, 1000);
});
 
async function getData() {
    const data = await fetchData();
    console.log(data);
}
 
getData();

The async function getData uses await to pause execution until fetchData resolves. This approach makes asynchronous code look and behave more like synchronous code, enhancing readability and maintainability.


Context of Promises with Async/Await

  • Understanding how to use async/await within the context of promises is crucial for effective asynchronous programming in JavaScript.
const fetchData = (isError) => new Promise((resolve, reject) => {
    setTimeout(() => {
        if (isError) {
            reject("Error occurred");
        } else {
            resolve("Data received");
        }
    }, 1000);
});
 
async function getData() {
    try {
        const data = await fetchData(false);
        console.log(data);
    } catch (error) {
        console.error(error);
    }
}
 
getData();

This example shows how async/await is used in conjunction with promises. The try...catch block handles any errors that occur during the asynchronous operation, demonstrating effective error handling within an async function.