JavaScript

Promises

Topics

  1. Brief Review of Asynchronous Code
  2. What are Promises?
  3. How do functions that use Promises actually work?
  4. Why Promises?

Asynchronous JavaScript

Some JavaScript Methods are Asynchronous

console.log('this prints first');

setTimeout(function() {
  console.log('this prints last');
}, 0);

console.log('this prints second');

JavaScript code runs synchronously, but we have a way of handling async code: asynchronous callbacks.

This code does not run "out of order". However, the callback to setTimeout is not executed right away, it runs after a timer expires on something called the "event loop".

jQuery AJAX

var planet;

$.getJSON('https://swapi.co/api/planets/1/', function(response) {
    planet = response;
});

console.log(planet);

Let's try to pull some data with jQuery's AJAX request, which utilizes callbacks

What is the value of planet?

Why is it undefined?!

The console.log was synchronous; it ran before the asynchronous callback.

jQuery AJAX

var planet;

$.getJSON('https://swapi.co/api/planets/1/', function(response) {
    planet = response;
    console.log('done', planet);
});

console.log('waiting');

Let's fix it!

Asynchronous callbacks run after the rest of the code

Once you are inside the callback, the code executes predictably as per usual, (unless there are more async callbacks in there)

Takeaways

  1. JavaScript code is executed synchronously (in-order).
  2. JavaScript can use special asynchronous callbacks to delay the execution of certain pieces of code.
  3. Not all callbacks are asynchronous; you'll have to consult their documentation to tell.

Check out MDN's discussion of callbacks.

What

are

Promises?

Promise

A Promise is a new global object (as of ES6) that serves as a placeholder for asynchronous operations.

fetch('https://httpbin.org/get');
// -> Promise {<pending>}

fetch is a modern web API built-in to the browser that uses promises.

Promise Syntax

Promises provide a .then and a .catch, which both accept callbacks.

fetch('https://httpbin.org/get')
  .then(function(response) {
    // the Promise resolved successfully
    console.log(response);
  })
  .catch(function(error) {
    // the Promise was rejected
    console.log(error);
  });

Promise Chaining

You can chain .then statements together by having more promises, but you only ever need 1 .catch method at the end.

fetch('https://pokeapi.co/api/v2/pokemon/1/')
  .then(function(response) {
    return response.json();
  })
  .then(function(pokemon1) {
    console.log(`The first pokemon is ${pokemon1.name}`);
    return fetch('https://pokeapi.co/api/v2/pokemon/2/');
  })
  .then(function(response) {
    return response.json();
  })
  .then(function(pokemon2) {
    console.log(`The second pokemon is ${pokemon2.name}`);
  })
  .catch(function(error) {
    // something bad happened
    console.log(error);
  });

Use Arrow Functions

with Promises

fetch('https://pokeapi.co/api/v2/pokemon/1/')
  .then(response => response.json())
  .then(pokemon1 => {
    console.log(`The first pokemon is ${pokemon1.name}`);
    return fetch('https://pokeapi.co/api/v2/pokemon/2/');
  })
  .then(response => response.json())
  .then(pokemon2 => console.log(`The second pokemon is ${pokemon2.name}`))
  .catch(error => console.log(error));

Same exact functionality as the previous slide

jQuery Promises

jQuery .ajax and .get and .getJSON methods support a Promise-like* interface as well:

$.getJSON('https://pokeapi.co/api/v2/pokemon/')
  .then(pokemon => {
    console.log(`Holy crap there are ${pokemon.count} pokemon now.`);
  })
  .catch(err => {
    console.log(err);
  });

They are promises implemented before there were standard JavaScript promises.

Takeaways

  1. Promise is a global object used for asynchronous functions
  2. A promise represents a pending value (a guarantee that there will either be a resolved or rejected value)
  3. Standard promises all have a .then() method, which takes a callback of the resolved value, and this can be chained.
  4. Standard promises all also have a .catch() method, which takes a callback of the rejected value, and this can only be listed once at the end of a .then chain.
  5. The built-in fetch API and jQuery's ajax method both use promises.

How do

functions

that use

Promises

actually work?

Roll Our Own

Asynchronous Function

Let's make our own promise to see how they work:

function waitForSomeTime(ms) {
  return new Promise(function(resolve, reject) {
    try {
      setTimeout(function() {
        return resolve('Time is up!');
      }, ms);
    } catch (error) {
      return reject(error);
    }
  });
}

waitForSomeTime(1000).then(res => console.log(res))

Asynchronous

Function Recipe

function myAsyncFunction() {
  // return a new Promise
  return new Promise((resolve, reject) => {
    /*

       DO ASYNC STUFF HERE

    */

    // if it succeeds, call the resolve callback
    resolve(/* success value*/);

    // if it fails, call the reject callback
    reject(/* fail value*/)
  })
}

Why do

we have

Promises?

Callback Hell

Pokemon Callback Hell

$.getJSON('https://pokeapi.co/api/v2/pokemon/1/', pokemon1 => {
  console.log(`The first pokemon is ${pokemon1.name}`);
  return $
    .getJSON('https://pokeapi.co/api/v2/pokemon/2/', pokemon2 => {
      console.log(`The second pokemon is ${pokemon2.name}`);
      return $
        .getJSON('https://pokeapi.co/api/v2/pokemon/3/', pokemon3 => {
          console.log(`The third pokemon is ${pokemon3.name}`);
          return $
            .getJSON('https://pokeapi.co/api/v2/pokemon/4/', pokemon4 => {
              console.log(`The fourth pokemon is ${pokemon4.name}`);
            })
            .fail(error => console.log(error));
        })
        .fail(error => console.log(error));
    })
    .fail(error => console.log(error));
}).fail(error => console.log(error));

Pokemon with Promises

$.getJSON('https://pokeapi.co/api/v2/pokemon/1/')
  .then(pokemon1 => {
    console.log(`The first pokemon is ${pokemon1.name}`);
    return $.getJSON('https://pokeapi.co/api/v2/pokemon/2/');
  })
  .then(pokemon2 => {
    console.log(`The second pokemon is ${pokemon2.name}`);
    return $.getJSON('https://pokeapi.co/api/v2/pokemon/3/');
  })
  .then(pokemon3 => {
    console.log(`The third pokemon is ${pokemon3.name}`);
    return $.getJSON('https://pokeapi.co/api/v2/pokemon/4/');
  })
  .then(pokemon4 => {
    console.log(`The fourth pokemon is ${pokemon4.name}`);
  })
  .catch(error => console.log(error));

Promise.all

let fourPokemonPromises = [];

for (let i = 1; i < 5; i++) {
  fourPokemonPromises.push(
    $.getJSON(`https://pokeapi.co/api/v2/pokemon/${i}/`)
  );
}

Promise.all(fourPokemonPromises)
  .then(pokemon => pokemon.forEach(p => console.log(p.name)))
  .catch(err => console.log(err));

Promise.all accepts an array of promises and will resolve when all of them pass or reject when any of them fail

This effectively lets you make several AJAX calls simultaneously, which can greatly improve the speed of your app.

Promise.race

var fourPokemonRace = [];

for (let i = 1; i < 5; i++) {
  fourPokemonRace.push($.getJSON(`https://pokeapi.co/api/v2/pokemon/${i}/`));
}

Promise.race(fourPokemonRace)
  .then(pokemon => console.log(pokemon.name))
  .catch(err => console.log(err));

Promise.race accepts an array of promises and resolves or rejects the first one to finish.

When might this be useful?

Advanced

Promise Stuff

$.getJSON('https://swapi.co/api/planets/1/')
  .then(res => {
    let planet = res;
    let filmsPlanetWasIn = planet.films;
    return Promise.all(filmsPlanetWasIn.map(film => $.getJSON(film)));
  })
  .then(films => console.log(`Tattoine was in the following films: ${films}`))
  .catch(err => console.log('Uh oh', err));

Here is a combination of what we've learned; making a request to get data used to make a handful of requests efficiently.

Takeaways

  1. Promises can reduce the clutter of "callback hell" by utilizing promise-chaining, which has a clearer syntax
  2. Promises have advanced features like Promise.all and Promise.race that are much harder to implement using callbacks
  3. Many popular asynchronous JavaScript APIs are set up to use Promises, including jQuery's .ajax method, fetch, axios, and more

JavaScript Promises

By Michael Hueter

JavaScript Promises

An introduction to Promises in JavaScript

  • 976