Deep Dive into JavaScript Promises: Patterns and Best Practices

JavaScript promises are a powerful tool for handling asynchronous operations. They represent a value that may be available now, or in the future, or never. Understanding how to effectively use promises can significantly improve the quality and readabil…


This content originally appeared on DEV Community and was authored by Delia

JavaScript promises are a powerful tool for handling asynchronous operations. They represent a value that may be available now, or in the future, or never. Understanding how to effectively use promises can significantly improve the quality and readability of your code. This article will explore the intricacies of JavaScript promises, common patterns, and best practices.

What is a Promise?

A promise is an object representing the eventual completion or failure of an asynchronous operation. It allows you to attach handlers for the eventual success or failure of that operation.

Basic Promise Syntax

let promise = new Promise(function(resolve, reject) {
  // executor (the producing code, "singer")
});

States of a Promise

  • Pending: Initial state, neither fulfilled nor rejected.
  • Fulfilled: The operation completed successfully.
  • Rejected: The operation failed.

Creating a Promise

A promise is created using the new Promise constructor which takes a function (executor) with two arguments: resolve and reject.

let promise = new Promise((resolve, reject) => {
  setTimeout(() => resolve("done"), 1000);
});

Consuming Promises

You consume promises using then, catch, and finally methods.

promise
  .then(result => console.log(result)) // "done" after 1 second
  .catch(error => console.error(error))
  .finally(() => console.log("Promise finished"));

Common Patterns with Promises

1. Chaining Promises

Promise chaining is a pattern where each then returns a new promise, making it easy to perform a series of asynchronous operations sequentially.

fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => {
    console.log(data);
    return fetch('https://api.example.com/other-data');
  })
  .then(response => response.json())
  .then(otherData => console.log(otherData))
  .catch(error => console.error('Error:', error));

2. Error Handling

Proper error handling in promises ensures that you can catch errors at any point in the chain.

promise
  .then(result => {
    throw new Error("Something went wrong");
  })
  .catch(error => {
    console.error(error.message);
  });

3. Parallel Execution with Promise.all

When you need to run multiple asynchronous operations in parallel, use Promise.all.

Promise.all([
  fetch('https://api.example.com/data1'),
  fetch('https://api.example.com/data2')
])
  .then(responses => Promise.all(responses.map(res => res.json())))
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

4. Promise.race

Promise.race returns a promise that resolves or rejects as soon as one of the promises in the iterable resolves or rejects.

Promise.race([
  new Promise(resolve => setTimeout(resolve, 100, 'one')),
  new Promise(resolve => setTimeout(resolve, 200, 'two'))
])
  .then(value => console.log(value)); // "one"

Best Practices with Promises

1. Always Return a Promise

When working within a then handler, always return a promise. This ensures that the next then in the chain waits for the returned promise to resolve.

fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => {
    return fetch('https://api.example.com/other-data');
  })
  .then(response => response.json())
  .then(otherData => console.log(otherData));

2. Use catch for Error Handling

Always use catch at the end of your promise chain to handle errors.

fetch('https://api.example.com/data')
  .then(response => response.json())
  .catch(error => console.error('Fetch error:', error));

3. Use finally for Cleanup

Use finally to execute code that should run regardless of whether the promise is fulfilled or rejected.

promise
  .then(result => console.log(result))
  .catch(error => console.error(error))
  .finally(() => console.log('Cleanup'));

4. Avoid the Promise Constructor Anti-Pattern

Don't use the promise constructor when you can use existing promise-based APIs.

Anti-Pattern:

new Promise((resolve, reject) => {
  fs.readFile('file.txt', (err, data) => {
    if (err) reject(err);
    else resolve(data);
  });
});

Better:

const {promises: fsPromises} = require('fs');
fsPromises.readFile('file.txt');

Examples of Promises in Real-World Scenarios

Example 1: Fetching Data from an API

function fetchData() {
  return fetch('https://jsonplaceholder.typicode.com/todos/1')
    .then(response => {
      if (!response.ok) {
        throw new Error('Network response was not ok');
      }
      return response.json();
    })
    .then(data => console.log(data))
    .catch(error => console.error('Fetch error:', error));
}

fetchData();

Example 2: Sequential API Calls

function fetchUserAndPosts() {
  return fetch('https://jsonplaceholder.typicode.com/users/1')
    .then(response => response.json())
    .then(user => {
      console.log(user);
      return fetch(`https://jsonplaceholder.typicode.com/posts?userId=${user.id}`);
    })
    .then(response => response.json())
    .then(posts => console.log(posts))
    .catch(error => console.error('Error:', error));
}

fetchUserAndPosts();

Promises are a cornerstone of modern JavaScript, enabling developers to handle asynchronous operations with greater ease and readability. By understanding the various patterns and best practices, you can write more efficient and maintainable code. Remember to always handle errors properly, return promises in then handlers, and utilize Promise.all and Promise.race for parallel and competitive asynchronous tasks. With these techniques, you'll be well-equipped to tackle complex asynchronous programming challenges.


This content originally appeared on DEV Community and was authored by Delia


Print Share Comment Cite Upload Translate Updates
APA

Delia | Sciencx (2024-06-24T15:15:26+00:00) Deep Dive into JavaScript Promises: Patterns and Best Practices. Retrieved from https://www.scien.cx/2024/06/24/deep-dive-into-javascript-promises-patterns-and-best-practices/

MLA
" » Deep Dive into JavaScript Promises: Patterns and Best Practices." Delia | Sciencx - Monday June 24, 2024, https://www.scien.cx/2024/06/24/deep-dive-into-javascript-promises-patterns-and-best-practices/
HARVARD
Delia | Sciencx Monday June 24, 2024 » Deep Dive into JavaScript Promises: Patterns and Best Practices., viewed ,<https://www.scien.cx/2024/06/24/deep-dive-into-javascript-promises-patterns-and-best-practices/>
VANCOUVER
Delia | Sciencx - » Deep Dive into JavaScript Promises: Patterns and Best Practices. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2024/06/24/deep-dive-into-javascript-promises-patterns-and-best-practices/
CHICAGO
" » Deep Dive into JavaScript Promises: Patterns and Best Practices." Delia | Sciencx - Accessed . https://www.scien.cx/2024/06/24/deep-dive-into-javascript-promises-patterns-and-best-practices/
IEEE
" » Deep Dive into JavaScript Promises: Patterns and Best Practices." Delia | Sciencx [Online]. Available: https://www.scien.cx/2024/06/24/deep-dive-into-javascript-promises-patterns-and-best-practices/. [Accessed: ]
rf:citation
» Deep Dive into JavaScript Promises: Patterns and Best Practices | Delia | Sciencx | https://www.scien.cx/2024/06/24/deep-dive-into-javascript-promises-patterns-and-best-practices/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.