DEV Community

Yu Hamada
Yu Hamada

Posted on

Understanding Promises in 100 Seconds

What is Promises?

Promises are the foundation of asynchronous processing in modern JavaScript. A promise is an object returned by an asynchronous function, which represents the current state of the operation.

Asynchronous processing

Asynchronous processing enables various workflow processes to run at the same time.

Image description

There states of Promises

  • Pending - Initial state, The operation is still going on.
  • Fulfilled - Meaning that the operation was completed successfully.
  • Rejected - Meaning that the operation failed.

Anatomy of Promises

new Promise(function(resolve, reject) {
    resolve('Success');
});

new Promise(function(resolve, reject) {
    reject('Failed');
});
Enter fullscreen mode Exit fullscreen mode

The promise constructor takes one argument which is a callback with two parameters, resolve(success) and reject(error). Do whatever code you need inside the callback function. And you have to resolve().
reject() is optional if you don’t have errors.

Why do we use Promises?

Asynchronous operations were typically handled using callback functions, which can lead to deeply nested code structures, often referred to as callback hell or pyramid of doom. This makes the code harder to read, maintain, and debug.
Here is an example.

// Task 1
setTimeout(() => {
  console.log("Task 1 completed");

  // Nested Task 2
  setTimeout(() => {
    console.log("Task 2 completed");

    // Nested Task 3
    setTimeout(() => {
      console.log("Task 3 completed");

      // Nested Task 4
      setTimeout(() => {
        console.log("Task 4 completed");
        // This could go on...

      }, 1000); // End of Task 4

    }, 1000); // End of Task 3

  }, 1000); // End of Task 2

}, 1000); // End of Task 1
Enter fullscreen mode Exit fullscreen mode

Promises provide a more linear and readable code structure by chaining .then() handlers.
Here is the improved codes.

function simulateAsyncTask(taskName, duration) {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log(taskName + ' completed');
      resolve();
    }, duration);
  });
}

// Using the function to chain tasks
simulateAsyncTask('Task 1', 1000)
  .then(() => simulateAsyncTask('Task 2', 1000))
  .then(() => simulateAsyncTask('Task 3', 1000))
  .then(() => simulateAsyncTask('Task 4', 1000));
Enter fullscreen mode Exit fullscreen mode

How to use Promises

There's two ways to use Promises.

  1. Using then() and catch()
  2. Using async, await

then(), catch()

Every codes inside the .then() callback happens when the promise is resolved. The second "then" will NOT execute until first one is done. This connection with the “then” word is called a Promise chain.
On the other hand, every codes inside the .catch() callback happens when the promise is rejected.
Here is an example.

const myPromise = new Promise((resolve, reject) => {
  let condition = true;
  if (condition) {
    resolve('Promise resolved successfully.');
  } else {
    reject('Promise rejected.');
  }
});

myPromise
  .then((message) => {
    console.log(message);
  })
  .catch((error) => {
    console.error(error);
  });

Enter fullscreen mode Exit fullscreen mode

"myPromise" is a promise that resolves if condition is true, and rejects otherwise. .then() is used to handle the successful resolution of the promise, logging the success message to the console. .catch() is used to handle the case where the promise is rejected, logging the error message to the console.

async, await

Async/Await can also do the same thing like using "then". It's cleaner and readable. When you use await, you must put the async in front of the function. The second await will NOT execute until the first one is done.
Here is an example.

const myPromise = new Promise((resolve, reject) => {
  let condition = true;
  if (condition) {
    resolve('Promise resolved successfully.');
  } else {
    reject('Promise rejected.');
  }
});

// Here is changed!
async function handleMyPromise() {
  try {
    const message = await myPromise;
    console.log(message);
  } catch (error) {
    console.error(error);
  }
}

handleMyPromise();

Enter fullscreen mode Exit fullscreen mode

We create "myPromise" resolve if a condition is true and reject otherwise. The "'handleMyPromise" function is declared as async, which allows us to use await within it. The await pauses the function's execution until the promise is either resolved or rejected. If myPromise resolves, we print its resolved value. If it rejects, we catch and log the error.

Use case

One of the use cases of Promises is fetching API.
Here is an example.

function fetchDummyData() {
  const dummyUrl = 'https://example.com/dummy.json';

  fetch(dummyUrl)
    .then(response => {
      if (!response.ok) {
        throw new Error('Network response was not ok');
      }
      return response.json();
    })
    .then(data => {
      console.log('Fetched dummy data:', data);
    })
    .catch(error => {
      console.error('Fetching dummy data failed:', error);
    });
}

fetchDummyData();

Enter fullscreen mode Exit fullscreen mode

We call "fetch()" with a dummy URL. "fetch()" returns a Promise that resolves to the response of the request.
The first .then() receives the response. If the response is resolved, we call ".json()" which returns another Promise that resolves with the result of parsing the response body text as JSON. If not, we throw an error.
The second .then() receives the parsed JSON data, and we log it to the console. If there's an error, at any point in the chain, the .catch() callback function catches it, and we log the error.

Conclusion

Promises is an object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value.

Top comments (0)