{ Promises. }

Objectives:

By the end of this chapter, you should be able to:

  • Define what promises are and how they are constructed
  • Use promises to refactor asynchronous code
  • Compare and contrast callbacks and promises

Promises

Promises are almost always preferred over callbacks when managing asynchronous code. Promises make use of callback functions, but help avoid deep nesting of callbacks and improve readability. Another advantage of promises is that they are immutable, so once a promise is done, you can not accidentally call it again (something you can do accidentally with callbacks).

ES2015 introduces a native Promise API, but promises have been in use for quite a while. Different libraries have their own implementation of Promises (previously called "deferreds" in jQuery), but the idea is very similar between all of them.

Think of a promise as a future notice of information. Here's a simple example:

  1. You go to a cafe to buy a sandwich
  2. When you pay for the food and are given a "ticket" so that other people can order while you wait. This "ticket" is the idea of a promise (a future notice of information).
  3. You keep waiting and either:
    • Your ticket is called, you get your sandwich and you put the ticket in the trash (the promise is resolved and no one else can use the ticket)
    • Your ticket is called, but something went wrong with the order so you return your ticket (the promise is rejected)

Let's see what this looks like. Using the native Promise API, we can create a Promise using the Promise constructor. This constructor takes a single function as its argument, which represents the code that should be run. This code should also specify whether the promise should be resolved or rejected, by passing in resolve and reject functions as parameters.

function firstPromise(){
    return new Promise(function(resolve,reject){
        if(Math.random() > .5){
            resolve("You made it!");
        } else {
            reject("Your number was too low! Try again");
        }
    })
}

firstPromise().then(function(data){
    console.log(data);
}).catch(function(error){
    console.log(error);
})

Similarly, as of version 3, jQuery has built-in support for promises!

function getMovieData(title){
    return $.getJSON(`http://www.omdbapi.com/?t=${title}`);
}

getMovieData("Titanic").then(function(data){
    console.log("Here is our movie data!", data)
}).catch(function(err){
    console.log("Oops, something went wrong", err)
})

The other nice thing about Promises is that you can resolve multiple promises with the Promise.all function, which accepts an array of promises. When using Promise.all, the .then callback will not run until every promise inside of the array passed to Promise.all either resolves or one is rejected.

function getMovieData(title){
    return $.getJSON(`http://www.omdbapi.com/?t=${title}`);
}

Promise.all([getMovieData("Titanic"), getMovieData("Ghostbusters"), getMovieData("Sharknado")]).then(function(data){
    console.log(data);
}).catch(function(err){
    console.log("Oops, something went wrong!");
})

We can also return the value of promise to another. This is what allows us to escape out of callback hell. Rather than deeply nesting callbacks, we can chain together multiple promises using .then instead! Recall our callback example from the previous section:

$.getJSON("http://www.omdbapi.com/?t=titanic", function(data){
    console.log("Titanic - ", data);
    $.getJSON("http://www.omdbapi.com/?t=ghostbusters", function(data){
        console.log("Ghostbusters -", data);
        $.getJSON("http://www.omdbapi.com/?t=sharknado", function(data){
            console.log("Sharknado -", data);
        }, function(error){
            console.log("Oops something went wrong!", data);
        })
    }, function(error){
        console.log("Oops something went wrong!", data);
    })
}, function(error){
    console.log("Oops something went wrong!", data);
});

Here's how we can refactor this code the chainable nature of promises:

$.getJSON("http://www.omdbapi.com/?t=titanic", function(data){
    console.log("Titanic - ", data);
    return $.getJSON("http://www.omdbapi.com/?t=ghostbusters");
})
.then(function(data) {
    console.log("Ghostbusters -", data);
    return $.getJSON("http://www.omdbapi.com/?t=sharknado");
})
.then(function(data) {
    console.log("Sharknado -", data);
})
.catch(function(error) {
    console.log("Oops something went wrong!", data);
});

Not only have we eliminated the nesting, we've also reduced code duplication since we can catch all potential errors with a sincle .catch!

Here's another example that returns values from one promise to another promise:

function first(){
    return new Promise(function(resolve,reject){
        setTimeout(function(){
            console.log("first is done");
            resolve(10);
        },500)
    })
}

function second(previousPromiseData){
    return new Promise(function(resolve,reject){
         setTimeout(function(){
            console.log("second is done and we just got", previousPromiseData);
            resolve(previousPromiseData + 10);
         },500)
     })
}

function third(previousPromiseData){
    return new Promise(function(resolve,reject){
         setTimeout(function(){
            console.log("third is done and the total is", previousPromiseData);
            resolve();
         },500)
     })
}

first()
    .then(second)
    .then(third);

When you're ready, move on to Generators and Async Functions

Continue