top of page

Learn through our Blogs, Get Expert Help, Mentorship & Freelance Support!

Welcome to Colabcodes, where innovation drives technology forward. Explore the latest trends, practical programming tutorials, and in-depth insights across software development, AI, ML, NLP and more. Connect with our experienced freelancers and mentors for personalised guidance and support tailored to your needs.

Coding expert help blog - colabcodes

JavaScript Promises: A Beginner's Guide

  • Writer: Samul Black
    Samul Black
  • Feb 17
  • 5 min read

Updated: Jul 26

JavaScript is an asynchronous programming language, meaning it can execute multiple tasks at once without blocking the execution of other tasks. One of the most powerful features that enable this is Promises. In this blog, we will explore what promises are, how they work, and how you can use them to write clean and efficient asynchronous code in JavaScript.

JavaScript Promises - colabcodes

What is a Promise in JavaScript?

A Promise in JavaScript is an object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value. It acts as a placeholder for data that will be available at some point in the future.

Promises help in dealing with asynchronous operations like fetching data from an API, reading files, or executing database queries, making the code more readable and manageable compared to traditional callback-based approaches.

Promises were introduced in ES6 (ECMAScript 2015) as a more structured way to handle asynchronous operations. Before promises, developers relied heavily on callbacks, which often led to deeply nested and hard-to-maintain code, known as "callback hell." With promises, managing multiple asynchronous operations becomes much easier and more intuitive.


States of a Promise

A promise can be in one of the following three states:


  1. Pending - The initial state when the promise is neither fulfilled nor rejected.

  2. Fulfilled - The operation was successful, and the promise returns a resolved value.

  3. Rejected - The operation failed, and the promise returns an error or reason for the failure.


Once a promise is either fulfilled or rejected, it is considered settled, and its state does not change.

These states make it possible to write asynchronous code that behaves predictably. Unlike callbacks, where multiple handlers can be invoked unexpectedly, a settled promise only resolves once, ensuring a more structured flow of execution.


Creating a Promise

A promise is created using the Promise constructor. It takes a function (executor) as an argument, which itself takes two parameters: resolve and reject.

const myPromise = new Promise((resolve, reject) => {
    let success = true; // Change this to false to simulate rejection
    
    setTimeout(() => {
        if (success) {
            resolve("Operation was successful!");
        } else {
            reject("Operation failed!");
        }
    }, 2000);
});

Creating a promise allows you to encapsulate an asynchronous operation inside an object, which can then be consumed by other parts of your code. This separation of concerns helps in managing complex applications effectively.


Handling Promises

Promises use .then() and .catch() methods to handle success and failure, respectively.


Using .then() and .catch()

myPromise
    .then(response => {
        console.log("Success:", response);
    })
    .catch(error => {
        console.error("Error:", error);
    });

By using .then(), we can specify what should happen when the promise resolves successfully. The .catch() method allows us to handle any errors that occur during the execution of the promise.


Using .finally()

The .finally() method is used to execute code regardless of whether the promise was resolved or rejected.

myPromise
    .then(response => console.log("Success:", response))
    .catch(error => console.error("Error:", error))
    .finally(() => console.log("Promise has been settled"));

This is useful for cleanup operations such as closing database connections, hiding loading spinners, or resetting UI states.


Chaining Promises

You can chain multiple .then() methods to execute a sequence of asynchronous operations.

fetch('https://jsonplaceholder.typicode.com/posts/1')
    .then(response => response.json())
    .then(data => console.log("Post title:", data.title))
    .catch(error => console.error("Error fetching data:", error));

Promise chaining helps in maintaining code readability and ensures a linear execution of multiple asynchronous operations. Instead of deeply nested callbacks, each .then() returns a new promise, making it easy to follow the data flow.


Using async and await

Modern JavaScript provides an even cleaner way to work with promises using async and await.

async function fetchData() {
    try {
        let response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
        let data = await response.json();
        console.log("Post title:", data.title);
    } catch (error) {
        console.error("Error fetching data:", error);
    }
}
fetchData();

The async keyword allows a function to return a promise implicitly, while await pauses execution until the promise resolves. This results in code that looks synchronous while still being asynchronous under the hood.


Promise Methods

JavaScript provides several built-in methods to work efficiently with promises. These methods help handle multiple asynchronous operations, manage results, and catch errors effectively. Let’s explore the most commonly used Promise methods.


Promise.all()

Executes multiple promises in parallel and resolves when all promises are resolved. If any promise is rejected, it immediately rejects.

Promise.all([
    fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(res => res.json()),
    fetch('https://jsonplaceholder.typicode.com/posts/2')
.then(res => res.json())])
.then(results => console.log("Both posts:", results))
.catch(error => console.error("Error in one of the promises:", error));

This method is useful when you need multiple independent asynchronous operations to complete before moving forward.


Promise.race()

Resolves or rejects as soon as the first promise settles.

Promise.race([
    new Promise(resolve => setTimeout(() => resolve("First resolved!"), 1000)),
    new Promise(resolve => setTimeout(() => resolve("Second resolved!"), 2000))
])
.then(result => console.log("Winner:", result));

Promise.race() is helpful in cases where you want the fastest result among multiple promises, such as fetching data from multiple servers.


Promise.allSettled()

Waits for all promises to settle (either resolve or reject) and returns an array of their results.

Promise.allSettled([
    Promise.resolve("Success"),
    Promise.reject("Error"),
    Promise.resolve("Another Success")
])
.then(results => console.log("Results:", results));

Unlike Promise.all(), it does not fail if one of the promises is rejected.


Promise.any()

Returns the first promise that resolves successfully, ignoring rejections.

Promise.any([
    Promise.reject("Error 1"),
    Promise.reject("Error 2"),
    Promise.resolve("Success")
])
.then(result => console.log("First successful promise:", result))
.catch(error => console.error("All promises rejected:", error));

Conclusion

JavaScript promises are a powerful way to handle asynchronous operations efficiently. They provide a cleaner alternative to callbacks and enable better code readability, especially when used with async/await. By mastering promises and their associated methods, you can significantly improve the efficiency and maintainability of your JavaScript code. Understanding these concepts will help you write scalable and performant applications.

Additionally, promises provide a foundation for modern JavaScript frameworks and libraries that heavily rely on asynchronous operations, such as React, Angular, and Vue.js. Mastering promises will not only improve your core JavaScript skills but also make you a more proficient developer when working with modern web technologies.

Furthermore, understanding how to handle asynchronous operations effectively can lead to better performance optimisation, especially when dealing with network requests, file handling, and large-scale applications. By implementing the best practices for promises, such as proper error handling and chaining, you can avoid common pitfalls like unhandled promise rejections and memory leaks.

As JavaScript continues to evolve, promises remain a fundamental part of the language, and their integration with async/await has made them even more accessible. Whether you are developing client-side or server-side applications, a solid grasp of promises will empower you to build more responsive and efficient applications that provide a seamless user experience.


Get in touch for customized mentorship, research and freelance solutions tailored to your needs.

bottom of page