Promises, an introduction

Mattias Wikström
Go back

Asynchronous is almost as hard to spell as asynchronous code is hard to handle. Enter… Promises!


I’ve been spending the last week deep in the trenches of another course where we had an assignment to make a web scraper that should extract and analyze some information from three different web pages. Needless to say, I wrote my scraper in JavaScript using node and although the first few days were pretty confusing I got out of it unscathed and with some new knowledge:

So welcome, this is my (hopefully not totally incorrect) introduction to Promises in JavaScript. Have fun!

Setting the stage

var fruitDB = {
    'bananas': 3,
    'apples': 7,
    'coconuts': 1,
    'persimmons': 3
}

var getFruitSupply = fruit => {
    var amount = fruitDB[fruit];

    var returnString = `You have ${amount} ${fruit}.`;

    return returnString;
}

Here you can see our starting code: fruitDB is a faked database containing the amounts of different kinds of fruits and getFruitSupply is a function that takes a string name of a fruit and returns a string telling you how many of the requested fruit you have.

var bananaCount = getFruitSupply('bananas');

console.log(bananaCount);
// Logs the string "You have 3 bananas."

Link to JSBin

Above is an usage example. getFruitSupply is called to get the amount of bananas, the value is stored in the variable bananaCount and logged to the console. All is good and your monkey will be happy with his bananas, right?

The Problem

Well, kind of, but we are missing one extremely important part. In real life, the database will take a little while to give the data to you. Even if you have a good super-mega-fragilistic-fast broadband connection the request to the server won’t be as fast as the JavaScript parser. Let’s add the delay to our example code.

var getFruitSupply = fruit => {
    var amount = fruitDB[fruit];

    var returnString = `You have ${amount} ${fruit}.`;

    setTimeout(() => {
        return returnString;
    }, Math.random() * 2000);
}

Using setTimeout we have now added some faked response-time to our getFruitSupply-function, so lets try using it again:

var bananaCount = getFruitSupply('bananas');

console.log(bananaCount);
// Logs undefined

Link to JSBin

Even though the delay added to getFruitSupply isn’t that long the JavaScript parser doesn’t wait for it - it just keep on going. When the console.log is hit the getFruitSupple function still hasn’t returned a value and the bananaCount variable is still undefined.

No to mention how disappointed your monkey must feel, this is a real hassle to deal with. So let us add the magic ingredient to the mix.

Enter PROMISES

The Solution

var getFruitSupply = fruit => {
    var amount = fruitDB[fruit];

    var returnString = `You have ${amount} ${fruit}.`;

    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(returnString);
        }, Math.random() * 2000);
    });
}

Above you can see one way of using promises. In our getFruitSupply function, instead of returning our value inside of the setTimeOut we wrap the whole timeout in a Promise and return that instead.

A Promise takes a function as an argument, and this function is in turn called with the two aruments resolve and reject. These two arguments are functions provided by Promise to either return a value (resolve the Promise) or say that something went wrong (reject the Promise). Notice how in our setTimeOut instead of returning the returnString variable we resolve it using the resolve argument function that Promise provided.

Putting it simply it works like this: when getFruitSupply is called it returns a special Promise object that pretty much says “ok, hold on, I’ll get back to you soon, I promise, ok?”. When the promise has been resolved a value is returned.

Because what you return is the Promise and not the value itself you will have to change the usage also. This won’t work:

var bananaCount = getFruitSupply('bananas');

console.log(bananaCount);
// Logs something similar to "object Object {...}" or
// "Promise {...}" depending on where you run the code

Link to JSBin

Instead you have to use the .then() function of the promise. This function is called when the promise is resolved and is passed the resolved value as an argument. The correct way to use is like this:


var bananaCount = getFruitSupply('bananas');

bananaCount.then(value => console.log(value));
// Logs "You have 3 bananas" to the console."

Link to JSBin

Yippie, it works! But wait, there’s more!

The Magic

What if you want two get the amounts of more than one kind of fruit at the same time? Your monkey is gonna be hungry for apples and coconuts some times!

Let’s say we have an array of fruit we want to feed our monkey with:

var monkeyFood = [
    'bananas',
    'apples',
    'coconuts'
];

Using .map we can call our getFruitSupply on every fruit in the array like this:

var promisedFruits = monkeyFood.map(fruit => getFruitSupply(fruit));

console.log(promisedFruits);
// Logs something similar to
// [ Promise {...},
//   Promise {...},
//   Promise {...}]
// or
// [[object Object] { ... },
//  [object Object] { ... },
//  [object Object] { ... }]

Link to JSBin

The promisedFruit array now, instead of containing three strings, contains three promises.

So, what should we do with these? This looks really confusing… Do I have to do a forEach-loop or something and run the .then() function on every element? Should I just give up and order pizza for the monkey?

Please - calm down and take a seat, because this is where the real beauty of Promises shows itself.

Promise.all(promisedFruits)
    .then(values => console.log(values));
// Logs [ 'You have 3 bananas.',
//        'You have 7 apples.',
//        'You have 1 coconuts.' ]

Link to JSBin

Oh glory… It’s beautiful, isn’t it?

The Promise.all() function and takes an array of any number of promises and the .then() is called when all promises are resolved and is passed an array of all the returned values. So awesome, your monkey is going to love you!

And that about sums up my introduction to Promises in JavaScript. There are of course a lot of details that I didn’t talk about - and probably a lot I messed up! - so go to the Promises Page on MDN for more information.

Maybe I should also clarify this: Promises (as well as the arrow function and template strings that has been used in these examples) is a part of ES2015 and is not supported in all browsers right now (although it works great on the latest version of node!). But as most of you reading this are already doing programming in JSX for React you can probably make it work with your existing transpiler setup. Good luck!