Promise Class in JavaScript

Promise Class in JavaScript

In this article, we explain the Promise class in JavaScript.

YouTube Video

Promise

Promise class is a class for handling asynchronous operations. It handles results if the operation succeeds or error handling if it fails. Traditionally, callback functions were used when you needed to wait for an asynchronous operation to finish, but Promise makes this clearer and more powerful.

1const promise = new Promise((resolve, reject) => {
2    setTimeout(() => resolve("Done!"), 1000);
3});
4
5// Displays "Done!" after 1 second
6promise.then(result => console.log(result));
  • This code is an example of an asynchronous process that displays "Done!" after one second.
    • First, a new Promise object is created using new Promise. The argument is a function that takes two callbacks: resolve for success and reject for failure.
    • setTimeout is used to call resolve() after one second.
    • The then() method of the promise object waits for completion and displays the result.

Basic Structure

A Promise is an object that eventually results in either success or failure.

 1const promise = new Promise((resolve, reject) => {
 2    // Asynchronous operation
 3    // Flag indicating whether the operation was successful
 4    const success = true;
 5
 6    if (success) {
 7        resolve('Operation successful!');
 8    } else {
 9        reject('An error occurred');
10    }
11});
  • In the case of success, resolve() is called.
  • In the case of failure, reject() is called.

Promise object has three states.

1promise
2    .then((result) => {
3        console.log(result);
4    })
5    .catch((error) => {
6        console.error(error);
7    })
  • Pending
    • The asynchronous process has not yet completed.
  • Fulfilled
    • The asynchronous process succeeded, and the result was returned by resolve(). The result is received using the then() method.
  • Rejected
    • The asynchronous process failed, and an error occurred via reject(). The error is received using the catch() method.

then() and catch() Methods

Using a Promise, you can define what to do when an asynchronous operation completes. For this, you use the then() and catch() methods.

then()

1const promise = new Promise((resolve, reject) => {
2    resolve('Operation successful!');
3});
4
5promise.then((result) => {
6    console.log(result);  // "Operation successful!"
7});
  • The then() method specifies a function to be called when the Promise succeeds.

catch()

1const promise = new Promise((resolve, reject) => {
2    reject('An error occurred');
3});
4
5promise.catch((error) => {
6    console.error(error);  // "An error occurred"
7});
  • The catch() method specifies a function to be called when the Promise fails.

finally() Method

 1const promise = new Promise((resolve, reject) => {
 2    resolve('Operation successful!');
 3});
 4
 5promise
 6    .then((result) => {
 7        console.log(result);
 8    })
 9    .catch((error) => {
10        console.error(error);
11    })
12    .finally(() => {
13        console.log('The operation has completed');
14    });
  • The finally() method defines code that will be executed in the end regardless of whether the Promise succeeded or failed.

then() Chain

then() returns a new Promise, so you can chain the next then().

 1const promise = new Promise((resolve, reject) => {
 2    setTimeout(() => {
 3        resolve(1);
 4    }, 1000);
 5});
 6
 7promise
 8    .then(result => {
 9        console.log(result); // 1
10        return result + 1;
11    })
12    .then(result => {
13        console.log(result); // 2
14        return result + 1;
15    })
16    .then(result => {
17        console.log(result); // 3
18    })
19    .catch(error => {
20        console.error(error.message); // Something went wrong
21    });
  • In this code, the initial resolve() passes the value 1, and each value returned by a then is passed to the next then.
  • Using catch(), you can handle any error that occurs in the chain in one place.

Practical Example

For example, Promises are often used to fetch data from a server. Let's look at an example using the fetch() API.

 1fetch('https://codesparklab.com/json/example.json')
 2    .then(response => {
 3        if (!response.ok) {
 4            throw new Error('Network error');
 5        }
 6        return response.json();
 7    })
 8    .then(data => {
 9        console.log(data);
10    })
11    .catch(error => {
12        console.error('Error:', error);
13    });
  • In this code, since fetch() returns a Promise, then() is used to process the data after receiving a response from the server. Errors are handled using catch().

Handling Multiple Promises

Promise.all()

To execute multiple Promises simultaneously and proceed only if all succeed, use Promise.all().

 1const promise1 = Promise.resolve(1);
 2const promise2 = Promise.resolve(2);
 3const promise3 = Promise.resolve(3);
 4
 5Promise.all([promise1, promise2, promise3])
 6    .then((results) => {
 7        console.log(results);  // [1, 2, 3]
 8    })
 9    .catch((error) => {
10        console.error('Error :', error);
11    });
  • then() is called only if all Promises succeed. catch() is called if even one fails.

Promise.allSettled()

If you want to run multiple Promises simultaneously and get all results regardless of success or failure, use Promise.allSettled().

 1const promise1 = Promise.resolve(1);
 2const promise2 = Promise.reject("Failed");
 3const promise3 = Promise.resolve(3);
 4
 5Promise.allSettled([promise1, promise2, promise3])
 6    .then((results) => {
 7        results.forEach(result => console.log(result));
 8        // [
 9        //   { status: 'fulfilled', value: 1 },
10        //   { status: 'rejected', reason: 'Failed' },
11        //   { status: 'fulfilled', value: 3 }
12        // ]
13    });
  • then() is called once all the Promises have settled (either fulfilled or rejected). Each result includes a status property ('fulfilled' or 'rejected'). Even if some fail, it doesn't affect the other operations.

Promise.race()

Promise.race() returns the result of the Promise that settles first, whether fulfilled or rejected.

1const promise1 = new Promise((resolve) => setTimeout(resolve, 100, 'First'));
2const promise2 = new Promise((resolve) => setTimeout(resolve, 500, 'Second'));
3
4Promise.race([promise1, promise2]).then((result) => {
5    console.log(result);  // "First"
6});
  • As in this example, the result of the first completed Promise is returned.

Relationship with async/await

Using the async/await syntax allows you to write Promises more intuitively. An async function always returns a Promise, and await waits for the Promise to resolve.

 1async function fetchData() {
 2    try {
 3        const response = await fetch('https://codesparklab.com/json/example.json');
 4        const data = await response.json();
 5        console.log(data);
 6    } catch (error) {
 7        console.error('Error :', error);
 8    }
 9}
10
11fetchData();

By using async/await in this way, asynchronous operations can be written like synchronous code, making it easier to read.

Summary

  • Promise is an object for handling success or failure of asynchronous operations.
  • Handle successful operations with then() and errors with catch().
  • finally() defines code that is executed at the end regardless of success or failure.
  • You can handle multiple Promises together using Promise.all() or Promise.race().
  • async/await is syntax for writing Promises more simply.

You can follow along with the above article using Visual Studio Code on our YouTube channel. Please also check out the YouTube channel.

YouTube Video