Skip to content

26 Promises

Biswajit Sundara edited this page Aug 18, 2023 · 1 revision

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

  • It is a way to handle asynchronous code in a more structured and manageable manner.
  • Promise will ensure that control doesn't move to the next operation until the current one is resolved or rejected.
  • This behavior allows you to synchronize asynchronous code
  • The execution will wait until the Promise settles, either by being fulfilled or rejected.
  • And the execution will not move further until the promise is resolved.

1. Promise Object States

Execute the below code in a browser so we can see how it works under the hood.

A. Pending State

  • The initial state when a Promise is created, representing that the asynchronous operation is still in progress
  • and the final value is not yet available.
const output = new Promise((resolve, reject)=>{ 
});

console.log(output);

/* 
This will print this way
[prototype]: Promise
[promiseState]: Pending
[PromiseResult]: undefined
*/

B. Fulfilled State

  • The state that represents a successful completion of the asynchronous operation.
  • In this state, the Promise has a resolved value associated with it.
const output = new Promise((resolve, reject)=>{ 
      resolve('Hello')
});

console.log(output);

/* 
This will print this way
[prototype]: Promise
[promiseState]: fulfilled
[PromiseResult]: Hello
*/

C. Rejected State

  • The state that represents a failure or error during the asynchronous operation.
  • In this state, the Promise has a reason or error associated with it.
const output = new Promise((resolve, reject)=>{ 
      reject('Oops the server encountered an error')
});

console.log(output);

/* 
This will print this way
[prototype]: Promise
[promiseState]: rejected
[PromiseResult]: Oops the server encountered an error
*/

If we take an real world example, sending a http request to the server then it will end up in any of the below states

  • Success (Fulfilled) - If we get the data back from the server
  • Error (Rejected) - Oops the server encountered an error
  • Pending - The server couldn't fetch the data with the specified time

2. Promise Example

const fetchData = () =>{
    return new Promise((resolve, reject)=>{
       setTimeout(()=>{
            let data = { message: 'Data retrieved successfully' };
            resolve(data);
        }, 2000);

        setTimeout(()=>{
            // Reject the Promise with an error
            reject("Error while getting data from the server");
         },5000);
    })
}
  


fetchData()
  .then((result)=>{
    console.log(result);
  })
  .catch((error)=>{
     console.log(error);
   })
  • If we run above code. It will print the message Data is retrieved successfully because it returns the value in 2s.
  • To check the error step, if we decrease the time out value to 1000 from 5000 then it will be executed first
  • and then it will print the message Error while getting..
  • The promise state will be either fulfilled or pending and it can't be both.
  • note, once the promise object is returned, there's no further processing.

3. Promise Handling

  • If the promise is fulfilled then we can use .then() to extract the value
  • and if it's rejected then we can use .catch() to catch the error.
  • The .then() method is used to handle the promise, it accepts a callback function that will be executed when the Promise is fulfilled.
  • We can chain multiple .then() calls to handle the Promise's resolved value.
  • we can use the .catch() method to handle any errors that may occur during the Promise's execution.
  • If the Promise is rejected (by calling reject in the executor function or if an error occurs), the .catch() callback will be executed.
  • We can also have finally method, if we have something to execute in both fulfilled/rejected state.
fetchData()
  .then((result)=>{
    console.log(result);
  })
  .catch((error)=>{
    console.log(error);
   })
   .finally(()=>{
    console.log('Finally');
   })

4. Promise All

  • Let’s say we want many promises to execute in parallel and wait until all of them are ready.
  • For instance, download several URLs in parallel and process the content once they are all done.

Syntax: let promise = Promise.all([...promises...]);

  • Promise.all takes an array of promises and returns a new promise.
  • The new promise resolves when all listed promises are settled, and the array of their results becomes its result.
  • If we reduce the time out in the reject block in any one of the promises,
  • it will fail the entire thing and the promise.all will return the error object
const promiseNames = new Promise((resolve,reject) =>{
    setTimeout(()=>{
       resolve(["Kangna","Kapil","Kajol"]);
    },3000);

    setTimeout(()=>{
        reject("Error while getting data from the server");
     },5000);
});

const promiseSurnames = new Promise((resolve,reject) =>{
   setTimeout(()=>{
      resolve(["Ranaut","Sharma","Mukherjee"]);
   },3000);

   setTimeout(()=>{
       reject("Error while getting data from the server");
    },5000);
});

Promise.all([promiseNames,promiseSurnames]).then((data)=>{
   
   //Simply print the arrays
   console.log(data);

   //Use destructuring
   const [names, surnames] = data;
   for (let i=0;i<names.length; i++){
      const name = names[i];
      const surname = surnames[i]; 
      console.log(`${name} ${surname}`);
   }
}).catch((error)=>{
    console.log(error);
})

5. Then

  • To make the calls sequential we can do method chaining and using then we can extract values from promises.
const getRandomUsers = ((numberOfUsers)=>{
    const fetchUsers = fetch(`https://randomuser.me/api/?results=${numberOfUsers}`);
    console.log(fetchUsers);
    fetchUsers.then((response)=>{
        response.json().then((randomusers)=>{
             console.log(JSON.stringify(randomusers));
             console.log(JSON.stringify(randomusers.results.length));

             randomusers.results.array.forEach(user => {
                 const {gender, email} = user;
                 console.log(`${gender} ${email}`);
             });
        })
    })
})

getRandomUsers(5);

6. Promise Chaining

  • We can do promise chaining using the then operator
  • While forming a promise chain make sure to return the promise like return proceedToPayment(orderId);
const cart = ['Milk','Fruits','Veg'];

const createOrder = (cart) =>{
    const promise = new Promise((resolve)=>{
         resolve("123");
    })

    return promise;
}

const proceedToPayment = (orderId) =>{
  return new Promise((resolve, reject)=>{
    if(orderId){
        resolve("pay 500$");
    }
 })
}

const makePayment = (paymentInfo) =>{
  return new Promise((resolve, reject)=>{
    setTimeout(()=>{
      resolve("Payment Successful");
    },2000);
 })
}

createOrder(cart)
      .then((orderId)=>{
      console.log(orderId);
      return orderId;    
    })
    .then((orderId)=>{
      return proceedToPayment(orderId);
    })
    .then((paymentInfo)=>{
      return makePayment(paymentInfo);
    })
    .then((paymentStatus)=>{
       console.log(paymentStatus);
    })
    .catch((error)=>{
      console.log(error);
    })
  • To handle the errors we have catch method.
  • If we write it at the last then if any of the method fails in the chain it will abort
  • If we want to handle the error for a specific method, have it just after the then block
  • We can have the catch block after every then block also in the chain.

Notes

  • A Promise can be created using the Promise constructor, which takes a function (commonly referred to as the executor function) as an argument.
  • The executor function receives two parameters: resolve and reject.
  • Within the executor function, we perform the asynchronous operation and use resolve to fulfill the Promise
  • with a value or reject to reject it with an error.

Clone this wiki locally