What is Callback, Promises, and Async/Await in JavaScript

You are currently viewing What is Callback, Promises, and  Async/Await in JavaScript

Callbacks, Promises, and Async/Await are methods to handle the asynchronous behavior of your code in JavaScript. They allow your code to perform tasks like making network requests, reading files, or waiting for timers without blocking the execution of other code.

If you want to learn more about JavaScript functions, Click here

What is Callbacks in JavaScript?

Callbacks are functions passed as arguments to other functions and are invoked once an asynchronous operation is completed.

function getData(url, callback) {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                callback(null, JSON.parse(xhr.responseText));
            } else {
                callback(xhr.status, null);
            }
        }
    };
    xhr.send();
}

getData('https://jsonplaceholder.typicode.com/posts/1', function(error, data) {
    if (error) {
        console.error('Error:', error);
    } else {
        console.log('Data:', data);
    }
});

What is a callback hell?

Callback hell is a situation in programming where multiple callbacks are nested inside each other, making the code hard to understand, maintain, and debug. This usually happens when performing several asynchronous operations that depend on each other, leading to deeply nested levels of indentation.

doSomething(function(result1) {
  doSomethingElse(result1, function(result2) {
    doAnotherThing(result2, function(result3) {
      doSomethingMore(result3, function(result4) {
        // And so on...
      });
    });
  });
});

What is Promises in JavaScript?

Promises provide a cleaner and more manageable way to handle asynchronous operations. A promise represents the eventual completion (or failure) of an asynchronous operation and its resulting value.

Successful call completions are indicated by the resolve function call, and errors are indicated by the reject function call.

Basic code:

let promise = new Promise(function(resolve, reject) {
  // Code to execute
});

What are the three states of promise in JavaScript?

  1. Pending: The initial state. The promise is neither fulfilled nor rejected. A promise starts in the pending state until it is either fulfilled or rejected.
  2. Fulfilled: When a promise is successfully resolved, it transitions to the fulfilled state.
  3. Rejected: When a promise is rejected, it transitions to the rejected state. (usually an error object).

How promises are resolved and rejected?

let promise = new Promise(function(resolve, reject) {
    resolve("Done with execution.");
});
let promise = new Promise(function(resolve, reject) {
    reject(new Error('Error while execution.'));
});
function getData(url) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    resolve(JSON.parse(xhr.responseText));
                } else {
                    reject(xhr.status);
                }
            }
        };
        xhr.send();
    });
}

getData('https://jsonplaceholder.typicode.com/posts/1')
    .then(data => {
        console.log('Data:', data);
    })
    .catch(error => {
        console.error('Error:', error);
    });

What are the Promises Pros and Cons?

Pros:

  • Avoids Callback Hell: Promises chain then calls, making the code more readable.
  • Better Error Handling: Errors can be caught and handled at any point in the chain.

Cons:

  • Slightly more complex than callbacks for beginners.

What is promise chaining in JavaScript?

Sometimes, when you need to handle multiple asynchronous requests in sequence, you can use promise chaining. After the first promise resolves or rejects, the next operation in the sequence starts automatically. This chaining process allows you to attach subsequent actions directly, simplifying the handling of asynchronous operations.

fetch('https://jsonplaceholder.typicode.com/posts/1')
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    return response.json();
  })
  .then(post => {
    console.log('Post:', post);   
    return fetch(`https://jsonplaceholder.typicode.com/users/${post.userId}`);
  })
  .then(response => {   
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }   
    return response.json();
  })
  .then(user => {
    console.log('User:', user);  
    return fetch(`https://jsonplaceholder.typicode.com/posts/1/comments`);
  })
  .then(response => {    
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }  
    return response.json();
  })
  .then(comments => {
    console.log('Comments:', comments);
  })
  .catch(error => {
    console.error('There was a problem with the fetch operation:', error);
  });

What is Promise.all?

Promise.all() is a method that takes an iterable (like an array) of promises as input and returns a single promise. This returned promise:

  • Fulfills: when all promises in the iterable fulfill, creating an array of their resolved values in the same order as the input promises.
  • Rejects: immediately if any of the input promises reject, and the returned promise’s rejection reason will be the reason of the first rejected promise.
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values); // Output: [3, 42, 'foo']
});
const urls = [
  'https://jsonplaceholder.typicode.com/posts/1',
  'https://jsonplaceholder.typicode.com/posts/2',
  'https://jsonplaceholder.typicode.com/posts/3'
];

// Map each URL to a fetch promise
const promises = urls.map(url => fetch(url));
// Use Promise.all to wait for all fetch requests to complete
Promise.all(promises)
  .then(responses => {
    // responses is an array of response objects
    return Promise.all(responses.map(response => response.json()));
  })
  .then(posts => {
    // posts is an array of post objects
    console.log('All posts fetched:', posts);
  })
  .catch(error => {
    console.error('Error fetching posts:', error);
  });

What are the Static methods available in promise?

MethodDescriptionBehavior
Promise.allResolves when all promises in the iterable argument have resolved, or rejects with the reason of the first promise that rejects.Example: Useful for parallel tasks where all results are needed (e.g., fetching multiple resources).
Promise.any
Resolves as soon as any promise in the iterable resolves, or rejects if all promises reject.
Example: Ideal for scenarios where any of several tasks completing successfully is sufficient.
Promise.allSettledResolves after all promises have settled (either resolved or rejected). Provides an array of results with status (fulfilled or rejected).Example: Useful for handling multiple promises regardless of their individual outcomes.
Promise.raceResolves or rejects as soon as one of the promises in the iterable resolves or rejects.Example: Suitable for scenarios where you need the result of the fastest promise (e.g., race conditions).
Promise.resolveReturns a promise that is resolved with the given value. let promise = new Promise(function(resolve, reject) {
resolve(“Done with execution.”);
});
Promise.rejectReturns a promise that is rejected with the given reason.let promise = new Promise(function(resolve, reject) {
reject(new Error(‘Error while execution.’));
});

How to Use Async/Await in JavaScript

Async/Await makes asynchronous code easier to write and understand. It makes code that runs one step after another easier to read, like regular synchronous code. This helps programmers write and manage their code better.

The await keyword can only be used inside an async function. The await keyword makes the function pause the execution and wait for a resolved promise before it continues:

function fetchData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('Data received');
        }, 1000);
    });
}

async function getData() {
    try {
        const data = await fetchData();
        console.log(data);
    } catch (error) {
        console.error(error);
    }
}
getData();
async function myDisplay() {
  let myPromise = new Promise(function(resolve, reject) {
    setTimeout(function(){resolve("Hello!");},2000)
  });
 return await myPromise;
}

myDisplay().then(function name(params) {
    console.log(params)
})
async function getData() {
    let response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
    let data = await response.json();
    return data;
}
getData() 
You can share it on

Leave a Reply