Javascript Promises – Part I – Getting started

Javascript Promises – Part I – Getting started

Have you ever seen those javascript codes that keep adding .then() and .then() all over the place and you wonder what the hell is happenning? You are not alone my friend. Today I’m going to talk about these things called Promise and tell you what, after you understand this Promise thing, you’ll find it one of the coolest thing Javascript has ever made.

Who should read this article?

This article applies to both javascript on the browser and javascript in Node.JS. So anyone who uses either of those may find this article helpful.

Why Promise?

Back in history, Javascript is all about callbacks. When we tell the code to do some task, we don’t wait for it to finish, but instead, we pass a callback to it so that the code can call the callback when the task is done.
Now if you want the callback above to do another task and then do something with the result from this second task, you add another callback to the previous callback. And when this adds up, you may get 10 layers of callback in your code and now you can’t even understand your own code flow. This situation is usually referred to as “callback hell”.

Getting started

Ok, let’s get down to business. At the heart of JavaScript Promise is the Promise constructor function, which looks like this:

var mypromise = new Promise(function(resolve, reject){
    // asynchronous code to run here
    // call resolve() to indicate task successfully completed
    // call reject() to indicate task has failed 
});

We can think of it this way: each Promise has a “main” function, which is the code that actually does the job. This “main” function takes two parameters including a resolve function – which main will call when the job is successfully done; and a reject function – which main will call if there is any error while executing the job.

Let’s see the whole thing in action to better understand it.

Introduction to Promise

This is how a code with Promise might look like:

function getImage(url){
    return new Promise(function(resolve, reject){
        var img = new Image()
        img.onload = function(){
            resolve(url);
        };
        img.onerror = function(){
            reject(url);
        };
        img.src = url;
    });
}

What just happened? We’ve just created a function which returns a Promise. The Promise loads an image; and then if the loading succeeds, calls resolve with url as parameter and if failed, calls reject, also with url as parameter.

It’s normal if you don’t get the idea up until now. We’ll try to explain more in details in the several next paragraphs.

Now we can call this function like below:

getImage('doggy.jpg').then(function(successurl){
    console.log('successfully loaded: ' + successurl);
});

Let’s first understand what’ve just happenned.
After getImage return a Promise, we provide that Promise with a resolve function. The Javascript engine will then pass the url to the resolve function as stated in the main function of the Promise:

img.onload = function(){
    resolve(url);
};

Now that we have rough idea of what a Promise looks like, let’s take a look at a simpler example.

The easiest way to get a visualization on this is by opening a Google Chrome browser and open Developer Tools panel (usually by pressing F12 on Windows, or Command-Alt-I on Mac, or from the Menu -> More tools -> Developer tools). Navigate to console tab and let’s do the business.

Ok you don’t need incognito mode. I just open incognito mode out of habit, if you know what I mean ;))

Creating your first Promise

Paste this in your chrome’s console:

var p1 = new Promise(function(resolve, reject) {
    console.log('1');
    return 'value 1';
});

p1;

We’ve just create a Promise named p1 and then print it to console to see its status, which results in something like this in the console:

Promise { [[PromiseStatus]]: "pending", [[PromiseValue]]: undefined }

So p1‘s status is pending, and no PromiseValue is returned yet. That is because we haven’t called resolve nor reject while executing the main function
Let’s change p1 a little bit

var p1 = new Promise(function(resolve, reject) {
    console.log('1');
    resolve('value 1');
});

p1;

Notice that this time we’ve changed return 'value 1'; in the previous code block to resolve('value 1');. The console now returns with:

Promise { [[PromiseStatus]]: "resolved", [[PromiseValue]]: "value 1" }

Yay! The promise is now resolved, and PromiseValue is now "value 1".
Now let’s type this into your console:

var p2 = p1.then(function(val) {
    console.log('2: ' + val);
    return val;
});

p2;

The console returns with:

Promise { [[PromiseStatus]]: "resolved", [[PromiseValue]]: "value 1" }

What just happened? By calling p1.then, we specify the resolve function for p1. So now when the main function of p1 compeletes, it knows which resolve function to call. So now it call the function specified in then(), and pass p1.PromiseValue to that function as param (in this case val).

But wait, p1 already finished before the real resolve function was passed to p1.then. How did that resolve function be called?

So, Promise is a mechanism provided by javascript engine, and Javascript Engine is the one who called that resolve function. Let’s imagine that Javascript Engine has a timer that continuously check the status of Promise p1. When p1 finishes, it updates the p1.status to either resolved or rejected, and save the <PromiseValue so that it will use to pass to resolve or reject function later on. Then it checks if a real resolve function is specified. If no resolve function is specified, it just leave the Promise there and recheck in its next timer loop. Half an hour later, someone specifies the real resolve function for p1 by calling p1.then(resolveFunc). In its next timer loop, Javascript Engine finds out that p1 now has a real resolve function so Javascript Engine calls that function with p1.PromiseValue as the function’s first parameter.

Another fancy thing to notice in the previous example is that p2 is also a Promise. Technically, to return a Promise, that block of code should be rewritten as below:

var p2 = p1.then(function(val) {
    console.log('2: ' + val);
    return new Promise(function (resolve, reject) {
        resolve(val);
    })
});

By default, Promise.then() returns a Promise. But Promise.then() is smart enough to check if the passed in function returns a value, it will wrap that function into a Promise and the returned value of that function will be the PromiseValue. On the other hand, if the passed in function returns a Promise, Promise.then() will just forward that Promise as its returned object.

Therefore, in the previous block of code, when .then() finds out that the passed in function just return val; it wrap that function into a Promise and return the Promise instead. Later, when that function finishes, it knows that it would use the value returned from the function to assign to PromiseValue of the returned Promise (p2).

Now that p2 is a Promise, we can continue to use then() on it as below:

var p3 = p2.then(function(val) {
    console.log('3: ' + val);
    return val;
});

p3;

The console output should be like this:

Promise { [[PromiseStatus]]: "resolved", [[PromiseValue]]: "value 1" }

which means p3 is also a Promise, it has been resolved and its PromiseValue is "value 1", waiting to be passed on to the next Promise if there is any.

Ok that’s it for part I. Now you know what Promise is and what those .then() functions mean.

In the next parts, we will look more into error handling in Promise and how Promise‘s are chained.

Next in series: Javascript Promises – Part II – Handling errors

Subscribe
Notify of

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x