newsletter

Understanding Javascript Promises a Guide to Asynchronous Programming

4 min read
Copied

Introduction

Asynchronous programming is a must-have in modern JavaScript development, allowing developers to perform non-blocking operations, such as fetching data from a server, reading files, or executing time-consuming operations. JavaScript promises are a powerful feature introduced in ES6 (ECMAScript 2015) to manage asynchronous operations more efficiently and cleanly. This blog post is a guide to JavaScript promises, offering a clear understanding how promises can be used to handle asynchronous tasks in an elegant way.

Copied

What Are JavaScript Promises?

A Promise in JavaScript is an object that represents the eventual completion or failure of an asynchronous operation and its resulting value. A Promise is a proxy for a value that is not necessarily known when the promise is created, this value can be evaluated at some point in the future.

Promises can be in one of three states:

  • Pending: The initial state of a promise. The operation has not completed yet.
  • Fulfilled: The operation completed successfully, and the promise has a resolved value.
  • Rejected: The operation failed, and the promise has a reason for the failure.
Copied

Creating and Using Promises

To create a promise, you can use the Promise constructor, which has one argument - a function called the executor. The executor function is executed immediately by the Promise implementation and it has two function parameters: resolve and reject callbacks.

javascript
const promise = new Promise((resolve, reject) => { try { // Execute operation resolve("Operation succeeded"); } catch (e) { reject("Operation failed"); } });

When a promise starts executing it's in the Pending state by default. Later promise must be transferred to either Fulfilled or Rejected state.

resolve callback is used to transfer a promise to Fulfilled state and return a resulting value of the promise as parameter. reject callback is used to transfer a promise to Rejected state and return a failure result of the promise as parameter.

There are 2 ways to get the promise result:

  • using then() and catch() functions that hook to the promise
  • using async/await statements
Copied

Get Promise Result Using then() and catch() Functions

To get a result from a promise, you can call then() function for success scenarios and catch() function for handling failures. For example:

javascript
promise .then(result => { // "Operation succeeded" if successful console.log(result); }) .catch(error => { // "Operation failed" if an error occurred console.error(error); });

You can chain these methods to perform additional operations after the previous promise has completed.

javascript
function firstPromise() { return new Promise((resolve, reject) => { setTimeout(() => { console.log("First promise finished with result: 1"); resolve(1); }, 1000); }); } function secondPromise(previousResult) { return new Promise((resolve, reject) => { setTimeout(() => { console.log("Second promise finished with result: ", previousResult + 1); resolve(previousResult + 1); }, 1000); }); } function thirdPromise(previousResult) { return new Promise((resolve, reject) => { setTimeout(() => { console.log("Third promise finished with result: ", previousResult + 1); resolve(previousResult + 1); }, 1000); }); } firstPromise() .then(result => secondPromise(result)) .then(result => thirdPromise(result)) .then(finalResult => console.log("All promises completed with final result: ", finalResult)) .catch(error => console.error("An error occurred:", error));

Here we chain together 3 promises executing one after another. In this example "3" is printed to console as a final result. The final catch() function is called when an error occurs or reject() function is called in any of 3 promises.

Before the invention of Promises performing asynchronous operations anticipated using callbacks. This led to a bad code structure with deeply nested callbacks, especially when using multiple asynchronous operations one after another.

Here is how the previous example will look like when using callbacks:

javascript
function firstCallback(callback) { setTimeout(() => { console.log("First callback finished with result: 1"); callback(1); }, 1000); } function secondCallback(previousResult, callback) { setTimeout(() => { console.log("Second callback finished with result: ", previousResult + 1); callback(previousResult + 1); }, 1000); } function thirdCallback(previousResult, callback) { setTimeout(() => { console.log("Third callback finished with result: ", previousResult + 1); callback(previousResult + 1); }, 1000); } firstCallback(function(result) { secondCallback(result, function(newResult) { thirdCallback(newResult, function(finalResult) { console.log("All callbacks completed with final result: ", finalResult); }); }); });

Doesn't look pretty, right? That's why the invention of promises was a huge step further in asynchronous JavaScript development. But what if I told you - that it's not the best viable option nowadays? Let's explore what is the best option.

Copied

Get Promise Result Using async/await

async/await statements in JavaScript are a complete game changer in asynchronous development. ES2017 introduced async functions and the await keyword, that allow developers to write asynchronous code that looks and behaves a like synchronous code:

javascript
async function fetchDataAsync() { try { const response = await fetch("https://jsonplaceholder.typicode.com/posts"); const data = await response.json(); return data; } catch (error) { console.error("Failed to fetch data: ", error); } } const data = await fetchDataAsync(); console.log(data);

Here an async function returns a Promise<T>, that holds a data received from the API call. By using await keyword we get this data as a promise result.

After the line const data = await fetchDataAsync(); we can simply write more code as if all operations were executed synchronously. It looks perfect!

Now let's explore how we can update one of the previous examples where we chained 3 promises with a then() function, when using async/await statements:

javascript
function firstPromise() { return new Promise((resolve, reject) => { setTimeout(() => { resolve(1); }, 1000); }); } function secondPromise(previousResult) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(previousResult + 1); }, 1000); }); } function thirdPromise(previousResult) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(previousResult + 1); }, 1000); }); } try { const firstResult = await firstPromise(); console.log("First promise finished with result: ", firstResult); const secondResult = await secondPromise(firstResult); console.log("Second promise finished with result: ", secondResult); const thirdResult = await thirdPromise(secondResult); console.log("Third promise finished with result: ", thirdResult); console.log("All promises completed with final result: ", thirdResult); } catch (error) { console.error("An error occurred:", error); }

It looks really nice. This code is much more readable and maintainable because of using the async/await statement.

Copied

Summary

JavaScript promises are a vital feature for performing non-blocking operations, such as fetching data from a server, reading files, or executing time-consuming operations. It offers a more manageable and readable approach compared to old callback-based solutions.

You can use the async/await syntax to write even cleaner and more efficient JavaScript code.

Hope you find this newsletter useful. See you next time.

Whenever you're ready, here's how I can help you:

The .NET Senior Playbook is built to:

  • Fast-track you from junior or mid-level to senior
  • Keep you growing as a senior
  • Help you beat any .NET interview

Covers everything: C#, ASP.NET Core, EF Core, system design — answer each question first, reveal the solution, and a test after every chapter proves it stuck. Finish, and you earn a verifiable certificate for your LinkedIn.

The .NET Senior Playbook
Join 500+ developers

Not sure where you stand? Take the free .NET interview test:

  • Find out your real level — Junior to Senior+
  • A realistic mock .NET interview — across 13 areas of C#, .NET, ASP.NET Core and System Design

No credit card required. When you finish, you get a personalized report: your level, your strongest and weakest areas, and where to focus next — the perfect way to benchmark yourself before diving into the Playbook.

Start the free test

Master .NET and Software Architecture,
so AI can't replace you

Real-world architecture judgment, production trade-offs, and .NET in depth — the things that keep you relevant in the job market.

Plus a free PDF: 200 senior .NET interview questions on C#, ASP.NET Core, EF Core & System Design.

Join 28,000+ developers already reading
No spam. Unsubscribe any time.

Enjoyed this article? Share it with your network