Home Web Front-end JS Tutorial Promises in JavaScript: Understanding, Handling, and Mastering Async Code

Promises in JavaScript: Understanding, Handling, and Mastering Async Code

Sep 03, 2024 pm 02:18 PM

Promises in JavaScript: Understanding, Handling, and Mastering Async Code

Intro

I used to work as Java developer and I remember for the first time when I got in touch with promises in JavaScript. Even though the concept seemed simple, I still couldn’t fully grasp how Promises worked. It changed when I started to use them in projects and understood the cases they solved. Then came the AHA moment and all became more clear. Over time, Promises became a valuable weapon on my toolbelt. It is oddly satisfying when I can use them at work and solve the async handling between functions.

You probably first come across Promises when fetching data from an API, which is also the most common example. Recently, I’ve been interviewed, and guess what was the first question “Can you tell me the difference between Promise and Async Await?”. I welcome that because I see it as a good starting point to know better how the applicant understands how the mechanisms work. However, he or she is mostly using other libraries and frameworks. It let me write down the differences and describe good practices for handling async function errors.

What is the Promise

Let’s start with the initial question: “What is the Promise?” Promise is a placeholder for the value that we don’t know yet but we will get it as a result of asynchronous computation/function. If the promise goes well, then we will get the result. If the promise does not go well, then the promise will return an error.

A basic example of a Promise

Defining a Promise

You define Promise by calling its constructor and passing two callback functions: resolve and reject.

const newPromise = new Promise((resolve, reject) => {
    resolve('Hello');
    // reject('Error');
});
Copy after login

We call resolve function when we want to succesfully resolve the Promise. reject is for rejecting the promise in the case when an error occurs during evaluating our logic.

Retrieving the Promise Result

We use the built-in function then to get the Promise's result. It has two passed callbacks, result and error. The result is called when the Promise is successfully resolved by the function resolve. If the Promise isn’t resolved, the second function error is called. This function is triggered either by reject or by another error that’s thrown.

newPromise.then(result => {
    console.log(result); // Hello
}, error => {
    console.log("There shouldn't be an error");
});
Copy after login

In our example, we will get the result Hello because we successfully resolved the Promise.

Error handling of promises

When the Promise is rejected then is always invoked its second error callback.

const newPromise1 = new Promise((resolve, reject) => {
  reject('An error occurred in Promise1');
});

newPromise1.then(
  (result) => {
    console.log(result); // It is not invoked
  },
  (error) => {
    console.log(error); // 'An error occurred in Promise1'
  }
);
Copy after login

A more recommended approach for its clarity is to use the built-in catch method.

const newPromise2 = new Promise((resolve, reject) => {
  reject('An error occurred in Promise2');
});

newPromise2
  .then((result) => {
    console.log(result); // It is not invoked
  })
  .catch((error) => {
    console.log(error); // 'An error occurred in Promise2'
  });
Copy after login

catch method is chained and has provided its own error callback. It gets invoked when the Promise is rejected.

Both versions work well but the chaining is IMO more readable and it is handy when using other built-in methods that we cover further.

Chaining promises

The result of a promise could likely be another promise. In that case, we can chain an arbitrary number of then functions.

getJSON('categories.json')
    .then(categories => {
        console.log('Fetched categories:', categories);

        return getJSON(categories[0].itemsUrl);
    })
    .then(items => {
        console.log('Fetched items:', items);

        return getJSON(items[0].detailsUrl);
    })
    .then(details => {
        console.log('Fetched details:', details);
    })
    .catch(error => {
        console.error('An error has occurred:', error.message);
    });
Copy after login

In our example, it serves to narrow down the search results to get details data. Each then function can also have its error callback. If we care only about catching any error in the chain of calls, then we can leverage catch function. It will be evaluated if any of the Promises return an error.

Promise all

Sometimes we want to wait for the results of more independent promises and then act on the results. We can use the built-in function Promise.all if we don’t care about the order of how the Promises got resolved.

Promise.all([
    getJSON('categories.json'),
    getJSON('technology_items.json'),
    getJSON('science_items.json')
])
    .then(results => {
        const categories = results[0];
        const techItems = results[1];
        const scienceItems = results[2];

        console.log('Fetched categories:', categories);
        console.log('Fetched technology items:', techItems);
        console.log('Fetched science items:', scienceItems);

        // Fetch details of the first item in each category
        return Promise.all([
            getJSON(techItems[0].detailsUrl),
            getJSON(scienceItems[0].detailsUrl)
        ]);
    })
    .then(detailsResults => {
        const laptopDetails = detailsResults[0];
        const physicsDetails = detailsResults[1];

        console.log('Fetched laptop details:', laptopDetails);
        console.log('Fetched physics details:', physicsDetails);
    })
    .catch(error => {
        console.error('An error has occurred:', error.message);
    });
Copy after login

Promise.all takes an array of Promises and returns an array of results. If one of the Promises is rejected then Promise.all is rejected as well.

Racing promises

Another built-in functionality is Promise.race. It’s used when you have multiple asynchronous functions - Promises - and you want to race them.

Promise.race([
    getJSON('technology_items.json'),
    getJSON('science_items.json')
])
    .then(result => {
        console.log('First resolved data:', result);
    })
    .catch(error => {
        console.error('An error has occurred:', error.message);
    });
Copy after login

Execution of the Promises can take different times and Promise.race evaluates the first resolved or rejected Promise from the array. It is used when we don’t care about the order but we want the result of the fastest asynchronous call.

What is Async Await

As you can see, writing Promises requires a lot of boilerplate code. Luckily, we have the native Async Await feature, which makes using Promises even easier. We mark a function by the word async and by that, we say that somewhere in the code we will be calling asynchronous function and we should not wait for it. Then the async function is called with the await word.

Basic example of Async Await

const fetchData = async () => {
    try {
        // Fetch the categories
        const categories = await getJSON('categories.json');
        console.log('Fetched categories:', categories);

        // Fetch items from the first category (Technology)
        const techItems = await getJSON(categories[0].itemsUrl);
        console.log('Fetched technology items:', techItems);

        // Fetch details of the first item in Technology (Laptops)
        const laptopDetails = await getJSON(techItems[0].detailsUrl);
        console.log('Fetched laptop details:', laptopDetails);
    } catch (error) {
        console.error('An error has occurred:', error.message);
    }
};

fetchData();
Copy after login

Our fetchData is marked as async and it allows us to use await to handle asynchronous calls inside the function. We call more Promises and they will evaluated one after the other.

We use try...catch block if we want handle the errors. Rejected error is then caught in the catch block and we can act on it like logging the error.

What’s different

They are both features of JavaScript handling with asynchronous code. The main difference is in the syntax when Promises use chaining with then and catch but async await syntax is more in synchronous way. It makes it easier to read. Error handling for async await is more straightforward when it leverages try...catch block. This is a question that you can easily get at the interview. During the answer, you can get deeper into the description of both and highlight those differences.

Promise features

Of course, you can use all the features with async await. For example Promise.all.

const fetchAllData = async () => {
    try {
        // Use await with Promise.all to fetch multiple JSON files in parallel
        const [techItems, scienceItems, laptopDetails] = await Promise.all([
            getJSON('technology_items.json'),
            getJSON('science_items.json'),
            getJSON('laptops_details.json')
        ]);

        console.log('Fetched technology items:', techItems);
        console.log('Fetched science items:', scienceItems);
        console.log('Fetched laptop details:', laptopDetails);
    } catch (error) {
        console.error('An error occurred:', error.message);
    }
};
Copy after login

Practical use cases

Promises are a fundamental feature in JavaScript for handling asynchronous code. Here are the main ways it is used:

Fetching Data from APIs

As was already shown in the examples above, this is one of the most used use cases for Promises and you work with it daily.

Handling file operations

Reading and writing files asynchronously can be done using promises, especially by Node.js module fs.promises

import * as fs from 'fs/promises';

const writeFileAsync = async (filePath, content, options = {}) => {
    try {
        await fs.writeFile(filePath, content, options);
        console.log(`File successfully written to ${filePath}`);
    } catch (error) {
        console.error(`Error writing file to ${filePath}:`, error.message);
    }
};

const filePath = 'output.txt';
const fileContent = 'Hello, this is some content to write to the file!';
const fileOptions = { encoding: 'utf8', flag: 'w' }; // Optional file write options

writeFileAsync(filePath, fileContent, fileOptions);
Copy after login

Promise based libraries

Axios is library that you should be familiar with. Axios handles HTTP requests in client and is vastly used.

Express is a web framework for Node.js. It makes it easy to build web apps and APIs, and when you use promises with Express, your code stays clean and easy to manage.

Repository with examples

All the examples can be found at: https://github.com/PrincAm/promise-example

Summary

Promises are a fundamental part of JavaScript, essential for handling asynchronous tasks in web development. Whether fetching data, working with files, or using popular libraries like Axios and Express, you’ll frequently use promises in your code.

In this article, we explored what Promises are, how to define and retrieve their results, and how to handle errors effectively. We also covered key features like chaining, Promise.all, and Promise.race. Finally, we introduced async await syntax, which offers a more straightforward way to work with promises.

Understanding these concepts is crucial for any JavaScript developer, as they are tools you’ll rely on daily.

If you haven’t tried it yet, I recommend writing a simple code snippet to fetch data from an API. You can start with a fun API to experiment with. Plus, all the examples and code snippets are available in this repository for you to explore.

The above is the detailed content of Promises in JavaScript: Understanding, Handling, and Mastering Async Code. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Hot Topics

Java Tutorial
1664
14
PHP Tutorial
1268
29
C# Tutorial
1245
24
The Evolution of JavaScript: Current Trends and Future Prospects The Evolution of JavaScript: Current Trends and Future Prospects Apr 10, 2025 am 09:33 AM

The latest trends in JavaScript include the rise of TypeScript, the popularity of modern frameworks and libraries, and the application of WebAssembly. Future prospects cover more powerful type systems, the development of server-side JavaScript, the expansion of artificial intelligence and machine learning, and the potential of IoT and edge computing.

JavaScript Engines: Comparing Implementations JavaScript Engines: Comparing Implementations Apr 13, 2025 am 12:05 AM

Different JavaScript engines have different effects when parsing and executing JavaScript code, because the implementation principles and optimization strategies of each engine differ. 1. Lexical analysis: convert source code into lexical unit. 2. Grammar analysis: Generate an abstract syntax tree. 3. Optimization and compilation: Generate machine code through the JIT compiler. 4. Execute: Run the machine code. V8 engine optimizes through instant compilation and hidden class, SpiderMonkey uses a type inference system, resulting in different performance performance on the same code.

Python vs. JavaScript: The Learning Curve and Ease of Use Python vs. JavaScript: The Learning Curve and Ease of Use Apr 16, 2025 am 12:12 AM

Python is more suitable for beginners, with a smooth learning curve and concise syntax; JavaScript is suitable for front-end development, with a steep learning curve and flexible syntax. 1. Python syntax is intuitive and suitable for data science and back-end development. 2. JavaScript is flexible and widely used in front-end and server-side programming.

JavaScript: Exploring the Versatility of a Web Language JavaScript: Exploring the Versatility of a Web Language Apr 11, 2025 am 12:01 AM

JavaScript is the core language of modern web development and is widely used for its diversity and flexibility. 1) Front-end development: build dynamic web pages and single-page applications through DOM operations and modern frameworks (such as React, Vue.js, Angular). 2) Server-side development: Node.js uses a non-blocking I/O model to handle high concurrency and real-time applications. 3) Mobile and desktop application development: cross-platform development is realized through ReactNative and Electron to improve development efficiency.

How to Build a Multi-Tenant SaaS Application with Next.js (Frontend Integration) How to Build a Multi-Tenant SaaS Application with Next.js (Frontend Integration) Apr 11, 2025 am 08:22 AM

This article demonstrates frontend integration with a backend secured by Permit, building a functional EdTech SaaS application using Next.js. The frontend fetches user permissions to control UI visibility and ensures API requests adhere to role-base

Building a Multi-Tenant SaaS Application with Next.js (Backend Integration) Building a Multi-Tenant SaaS Application with Next.js (Backend Integration) Apr 11, 2025 am 08:23 AM

I built a functional multi-tenant SaaS application (an EdTech app) with your everyday tech tool and you can do the same. First, what’s a multi-tenant SaaS application? Multi-tenant SaaS applications let you serve multiple customers from a sing

From C/C   to JavaScript: How It All Works From C/C to JavaScript: How It All Works Apr 14, 2025 am 12:05 AM

The shift from C/C to JavaScript requires adapting to dynamic typing, garbage collection and asynchronous programming. 1) C/C is a statically typed language that requires manual memory management, while JavaScript is dynamically typed and garbage collection is automatically processed. 2) C/C needs to be compiled into machine code, while JavaScript is an interpreted language. 3) JavaScript introduces concepts such as closures, prototype chains and Promise, which enhances flexibility and asynchronous programming capabilities.

JavaScript and the Web: Core Functionality and Use Cases JavaScript and the Web: Core Functionality and Use Cases Apr 18, 2025 am 12:19 AM

The main uses of JavaScript in web development include client interaction, form verification and asynchronous communication. 1) Dynamic content update and user interaction through DOM operations; 2) Client verification is carried out before the user submits data to improve the user experience; 3) Refreshless communication with the server is achieved through AJAX technology.

See all articles