JavaScript Promises

25 February 2019 - 10:34am

Promises are probably my favourite thing in modern JavaScript. They're relatively simple, but they simplify the architecture of a JavaScript app immensely.

Not only do they protect us from passing callbacks up and down our chain of functions (a.k.a. callback hell), they simplify the entire function.

Synchronous code is usually easier to reason about than asynchronous code, and promises are the first step towards asynchronous code in a synchronous style.

It's much easier to reason about a function that takes a parameter and then returns a result, rather than a function that takes multiple parameters and does something to some of them.

How does it work?

If you've never used promises, here is a quick toy example. First, this is how one might handle loading an image:

function loadImage(url, success, failure)
{
    let image = new Image();

    image.addEventListener("load", function()
    {
        success(image);
    });

    image.addEventListener("error", function()
    {
        failure("Could not load image");
    });

    image.src = url;
}

loadImage("/images/logo.png", function(image)
{
    document.appendChild(image);
}, function(error)
{
    window.alert(error);
});

Alternatively, this is how you might go about it using a promise:

function loadImage(url)
{
    return new Promise(function(resolve, reject)
    {
        let image = new Image();

        image.addEventListener("load", function()
        {
            resolve(image);
        });

        image.addEventListener("error", function()
        {
            reject("Could not load image");
        });

        image.src = url;
    });
}

loadImage("/images/logo.png").then(function(image)
{
    document.appendChild(image);
}).catch(function(error)
{
    window.alert(error);
});

So, what else can you do?

Our toy example doesn't seem to be much of an improvement over callbacks, but imagine if you wanted to do multiple things after that image had been downloaded:

const LOGO_URL = "/images/logo.png";
let logoPromise = loadImage(LOGO_URL);

...

logoPromise.then(displayLogo);

...

logoPromise.then(fadeInMenu);

Or maybe that you have something you want to do only after several images have finished downloading:

let promises = [];

for(let url of ["image-1.png", "image-2.png", "image-5.png"])
{
    promises.push(loadImage(url));
}

Promise.all(promises).then(function(images)
{
    displayGallery(images);
});

If you want to know more, have a look at the MDN article on using Promises.