try-catch-FAIL

Failure is inevitable

NAVIGATION - SEARCH

Practical Promises in JavaScript - Making Promises

In part 1 of our series, we covered the basics of promises. We know what they are. We know how to use then to receive the value of a promise once it's available. We know how to use catch to be notified when something goes wrong. But we haven't yet created a promise! In this post, we'll learn how to create promises.

Creating Promises that Resolve (or Reject) Immediately

The easiest way to create a promise is using one of the factory methods on the Promise object itself. Want to make a promise that immediately resolves to a given value? Here you go:

const promise = Promise.resolve(1);

promise.then(value => console.log(`The value is ${value}`));

Simple, isn't it? Promise.resolve gives you back a Promise that is already resolved, so if anyone registers a callback with then, their callback will be immediately invoked.

What if you want to make a promise that immediately rejects?

const promise = Promise.reject('Error!');
promise.then(value => console.log('This is never going to be executed...'));
promise.catch(error => console.log(`But this will be!  Here's the value: ${error}`))

While these two functions might not seem overly useful, they are actually quite useful in some situations and can really simplify your code. We'll see some examples of that later in this series.

One interesting thing to note is that our promise is already resolved/rejected before anyone has called then or catch on the promise. That's an important concept: for any promise, the order of operations is irrelevant. You can call then before the promise has been resolved. The result will be the same. This applies to rejection and catch, too.

Creating Promises that Resolve Later

If all promises could do was resolve (or reject) immediately, there'd be no need for them. No, the real value in promises is of course that they can represent a value that will eventually be available. Many frameworks expose APIs that return promises, but what if we want to create one ourselves?

For that, we'll use the Promise(function(resolve,reject){...}) constructor.

The function we pass to our promise will be invoked immediately with two arguments: a callback that we are to execute when we are ready to resolve the promise, and another we can call if we need to reject the promise. We can also throw an error, which will implicitly reject our promise.

Let's try to make a Promise that will resolve later:

const promise = new Promise((resolve,reject) => {
    setTimeout(() => {
        resolve('success!');
    }, 1000);
});
promise.then(value => console.log(`Promise resolved: ${value}`));
console.log('Promise created...');

The function we passed to our promise uses the setTimeout function to schedule yet another function to be executed in 1 second. The value we pass to the resolve callback will be passed on to anyone that's waiting on a result from our promise. Here's what the output looks like:

Promise created...
(1 second later)
Promise resolved: success!

We could also reject the promise, too, which would cause any error callbacks to be hit, too:

const promise = new Promise((resolve,reject) => {
    setTimeout(() => {
        reject('failure!');
    }, 1000);
});
promise.then(value => console.log('This will never happen...'));
promise.catch(error => console.log(`Promise rejected: ${error}`));
console.log('Promise created...');

Combining Promises

What would we do if we wanted to wait for the result of more than one promise before we executed some code? That's easy! We can also make promises from other promises!

Let's say that we have two promises that will return values eventually...

const quickPromise = new Promise((resolve) => {
    setTimeout(() => {
        resolve('we are go!');
    }, 1000);    
});
const lessQuickPromise = new Promise((resolve) => {
    setTimeout(() => {
        resolve('good here!');
    }, 5000);    
});

If we want to wait until both are resolved, we can use the Promise.all function to create a new promise that will resolve once both promises have resolved:

const megaPromise = Promise.all([quickPromise, lessQuickPromise]);
megaPromise.then((results) => {
    console.log(`Value of first promise: ${results[0]}`)
    console.log(`Value of second promise: ${results[1]}`)
})

Notice that our then callback will receive an array of results, not a single result. Since our mega promise was created from two promises, our callback receives an array containing two items.

What if we want to resolve the promise when any of a set of promises resolves? For that, we can use Promise.race:

const racer1 = new Promise((resolve) => {
    setTimeout(() => {
        resolve('racer1');
    }, 5000);    
});
const racer2 = new Promise((resolve) => {
    setTimeout(() => {
        resolve('racer2');
    }, 1000);    
});

const racingPromise = Promise.race([racer1,racer2]);
racingPromise.then(result => console.log('We have a winer: ', result));

Promise.race returns a promise that resolves as soon as at least one of the promises we provide to it has resolved. Our then callback receives the result of that first promise. The others are lost unless we explicitly registered callbacks on the individual promises.

Up Next

Now we know the basics of using promises, we know how to create them, and we know how to combine them in new ways. In the next post, we'll look at combining dependent promises through chaining.

About Matt Honeycutt...

Matt Honeycutt is a software architect specializing in ASP.NET web applications, particularly ASP.NET MVC. He has over a decade of experience in building (and testing!) web applications. He’s an avid practitioner of Test-Driven Development, creating both the SpecsFor and SpecsFor.Mvc frameworks.

He's also an author for Pluralsight, where he publishes courses on everything from web applications to testing!

blog comments powered by Disqus