Promises

A promise is an Javascript object that has a job: go get a particular value. A promise can be in one of three states:

  • Pending: the promise is in the process of trying to retrieve the value

  • Fulfilled: the promise has successfully retrieved the value

  • Rejected: the promise was unable to retrieve the value

Analogy

“Imagine you are a kid. Your mom promises you that she’ll get you a new phone next week.”

You don’t know if you will get that phone until next week. Your mom can either really buy you a brand new phone, or she doesn’t, because she is not happy :(.

That is a promise. A promise has 3 states. They are:

  • Pending: You don’t know if you will get that phone

  • Fulfilled: Mom is happy, she buys you a brand new phone

  • Rejected: Mom is unhappy, she doesn’t buy you a phone

This example scenario is adapted from Javascript Promises for Dummies

Creating a Promise

When you create a new promise, you have to tell it what it's job is. Do this by giving it a callback function.

const myPromise = new Promise(myCallback)

So, in our phone example:

const newPhonePromise = new Promise(checkMomsMood);

The promise will pass 2 arguments into the callback: 1. a function to run if the value is successfully retrieved (resolve) 2. a function to run if the value is not successfully retrieved (reject)

const isMomHappy = true

const checkMomsMood = (resolve, reject) => {
  console.log('pending...');
  if (isMomHappy) {
      const phone = { brand: 'Samsung', color: 'black' }
      resolve(phone)
  } else {
      const reason = new Error('mom is not happy')
      reject(reason)
  }
}

const newPhonePromise = new Promise(checkMomsMood)

Once a promise is fulfilled, then the promise will represent the value it was sent to retrieve.

console.log(newPhonePromise)

Try changing isMomHappy to a false - what changes?

Consuming the Promise

Promises are generally consumed by attaching a .then().catch(). .then()

  • triggered by the resolve() function

  • handles what to do next with the retrieved data

    .catch()

  • triggered by the reject() function

  • handles the error

const isMomHappy = true

const checkMomsMood = (resolve, reject) => {
  console.log('pending...');
  if (isMomHappy) {
      const phone = { brand: 'Samsung', color: 'black' }
      resolve(phone)
  } else {
      const reason = new Error('mom is not happy')
      reject(reason)
  }
}

const newPhonePromise = new Promise(checkMomsMood)

const willIGetNewPhone = () => {
  newPhonePromise
  .then(newPhone => {
    console.log("look at this sweet new phone:", newPhone)
  })
  .catch(err => {
    console.log("did NOT get a new phone :(")
    console.log(err)
  })
}

willIGetNewPhone()

Run this code to see the .then() callback run. Change isMomHappy to false to see the .catch() in action!

Chaining Promises

You can chain multiple promises together using the .then() function. In the callback function of the .then(), just return the next promise you want to run:

const isMomHappy = true

const checkMomsMood = (resolve, reject) => {
  console.log('pending...');
  if (isMomHappy) {
      const phone = { brand: 'Samsung', color: 'black' }
      resolve(phone)
  } else {
      const reason = new Error('mom is not happy')
      reject(reason)
  }
}

// 1st promise
const newPhonePromise = new Promise(checkMomsMood)

// 2nd promise (we don't need a reject because we know it will only run if we got a new phone)
const showOff = phone => {
    const message = 'Hey friend, I have a new ' +
                phone.color + ' ' + phone.brand + ' phone'
    return Promise.resolve(message)
}

const willIGetNewPhone = () => {
  newPhonePromise
  .then(newPhone => {
    console.log(showOff(newPhone))
  })
  .catch(err => {
    console.log("did NOT get a new phone :(")
    console.log(err)
  })
}

willIGetNewPhone()

Asyncronicity

Note that promises are asynchronous. If we add the following console.log() statements, they wont necessarily run in the order we'd expect. Try it out!

const willIGetNewPhone = () => {
  console.log("before promises")
  newPhonePromise
  .then(newPhone => {
    console.log(showOff(newPhone))
  })
  .catch(err => {
    console.log("did NOT get a new phone :(")
    console.log(err)
  })
  console.log("after promises")
}

How would you edit this code so that the final console.log statement runs after the promises are fulfilled?

Exercise:

Imagine you are stuck at home with a sprained ankle, so you send your friend to the market to buy some pain medication. Your friend makes a promise to return with some pain medication for you. Once your friend has left for the market, the promise is pending. If your friend returns from the market with medication, the promise is fulfilled. If your friend returns from the market without medication, the promise is rejected.

Write code with promises to represent this scenario!

EVERYTHING BELOW THIS LINE IS ESSENTIALLY THE SAME CODE AS ABOVE, JUST GENERIC VERSIONS FOR FURTHER REVIEW IF DESIRED

Creating a Promise

When you create a new promise, you have to tell it what it's job is. Do this buy giving it a callback function.

var myPromise = new Promise(myCallback)

The promise will pass 2 arguments into the callback: 1. a function to run if the value is successfully retrieved (resolve) 2. a function to run if the value is not successfully retrieved (reject)

const myCallback = (resolve, reject)=>{
  console.log('pending...');
  if(valueToRetrieve) {
    resolve(valueToRetrieve);
  } else {
    reject('valueToRetrieve is falsey');
  }
}

let valueToRetrieve = "";
let myPromise = new Promise(myCallback);

Once a promise is fulfilled, then the promise will represent the value it was sent to retrieve.

console.log(myPromise);

Try changing valueToRetrieve to a falsey value - what changes?

Consuming the Promise

Promises are generally consumed by attaching a .then().catch(). .then()

  • triggered by the resolve() function

  • handles what to do next with the retrieved data

    .catch()

  • triggered by the reject() function

  • handles the error

const myCallback = (resolve, reject)=>{
  console.log('pending...');
  if(valueToRetrieve) {
    setTimeout(()=>{resolve(valueToRetrieve)}, 2000);
  } else {
    setTimeout(()=>{reject('valueToRetrieve is falsey')}, 3000);
  }
}

const consumePromise = () => {
    myPromise
    .then((retrievedValue)=>{ // will run if resolve() is called
        console.log("fulfilled! retrievedValue is:", retrievedValue);
    }).catch((err) =>{ // will run if reject() is called
        console.log("wah wah :( Error:", err);
    })
}

let valueToRetrieve = "!!!";
let myPromise = new Promise(myCallback);
consumePromise()

Run this code to see the .then() callback run. Change valueToRetrieve to a falsey value to see the .catch() in action!

Chaining Promises

You can chain multiple promises together using the .then() function. In the callback function of the .then(), just return the next promise you want to run:

const consumeTwoPromises = () => {
    myPromise
    .then((firstRetrievedValue)=>{
      return new Promise((resolve, reject)=>{
          console.log("first retrived value: "+firstRetrievedValue);
          if(firstRetrievedValue){
            resolve(firstRetrievedValue+"???")
          } else {
            reject("firstRetrievedValue is falsey");
          }
      })
    })
    .then((secondRetrievedValue)=>{ // will run if resolve() is called
        console.log("second retrieved value is:", secondRetrievedValue);
    })
    .catch((err)=>{ // will run if reject() is called
        console.log("wah wah :( Error:", err);
    })
}

let valueToRetrieve = "!!!";
let myPromise = new Promise(myCallback);
consumeTwoPromises()

Asyncronicity

Note that promises are asynchronous. If we add the following console.log() statements, they wont necessarily run in the order we'd expect. Try it out!

const consumeTwoPromises = () => {
    console.log("this is the console.log before promises")
    myPromise
    .then((firstRetrievedValue)=>{
      return new Promise((resolve, reject)=>{
          console.log("first retrived value: "+firstRetrievedValue);
          if(firstRetrievedValue){
            resolve(firstRetrievedValue+"???")
          } else {
            reject("firstRetrievedValue is falsey");
          }
      })
    })
    .then((secondRetrievedValue)=>{ // will run if resolve() is called
        console.log("second retrieved value is:", secondRetrievedValue);
    })
    .catch((err)=>{ // will run if reject() is called
        console.log("wah wah :( Error:", err);
    })
    console.log("this is the console.log written after promises")
}

How would you edit this code so that the final console.log statement runs after the promises are fulfilled?

More Resources

Last updated