How to Promise with JavaScript!!

A Beginner’s Guide to understand the promise concepts

Rajesh Kumar
9 min readAug 16, 2020

In this article, we will discuss about the fundamentals concepts of Javascript Promise and how we can handle the asynchronous operation with a JS promise which is quite complex to understand for many learners. So I will try to explain each concept with proper detailing and real-world example that will give you a better idea about Promise in a much easier way.

What is Promise in JS?

In simple words, a promise is a commitment by someone to do or not do something.

Exactly the same kind of thing Promise does in JS.

Let’s take a simple example scenario first.

Imagine you are a famous author having a huge fan following and your fans ask day and night for your next novels.

To get some relief, you promise to very soon you will publish your next novel. You asked your fans to subscribe and turn on the notification of your youtube channel so that when you will come to live on your channel to publish your novels, all subscribed fans instantly receive the info.

And even if something goes very wrong, due to that you will not able to publish your next novel, they will still be notified through your channel.

Now Everyone will be happy — you, because the people don’t crowd you anymore and fans because they won’t miss your next novel update in any case.

This is a real-life analogy to the things we often place in programming:-

  1. A “producing code” that does something and takes time. For instance, some code that loads the data over a network. Similarly, That’s an “AUTHOR”.
  2. A “consuming code” that wants the result of the “producing code” once it’s ready. Many functions may need that result. These are the “FANS”.

And the promise is a special JavaScript object that links the “producing code” and the “consuming code” together.

In terms of our analogy: this is the “subscription of the channel”. The “producing code” takes whatever time it needs to produce the promised result, and the “promise” makes that result available to all of the subscribed code when it’s ready.

I hope now you have some basic theoretical ideas about Promise.

But this is not the 100% exact accurate, because JS promises are more complex than a simple subscription thing — that have also many extra features and limitations as well.😒

But don’t worry I’ll try to explain each concept in a very detailed and easiest way!!. 😉

Now let’s begin with the construction and implementation of Promise.

Construction and implementation of Promise in JS.

The syntax to construct a promise object is as below:-

let promiseForNovel = new Promise(function(resolve,reject){
//will write work of executor(the producing code, "Author")
});

The function that is passed to new Promise is called as Executor. When we create new Promise object, the executor runs automatically. It contains the producing code which will produce the at the end result.

So in the case of terminology — The author is the Executor and the effort and other things required to publish the novels or information in case if something went wrong as Result is the “producing code”.

Its arguments resolve and reject are callbacks provided by JavaScript itself.

Note:- If you want to learn about callback you can go through my article.

When the executor obtains the result, be it soon or late, doesn’t matter, it should call one of these callbacks:

  • resolve(value) — if the job finished successfully, with the result value.
  • reject(error) — if an error occurred, error is the error object.

So basically — the executor runs automatically and attempts to perform a job. When it is finished with the attempt and if it was successful it calls resolve or if there was an error reject .

The promise object returned by the new Promise constructor has these internal properties:

  • state — initially "pending", then changes to either "fulfilled" when resolve is called"rejected" when reject is called.
  • result — initially undefined, then changes to value when resolve(value) called or error when reject(error) is called.

Let’s understand the above properties with a simple diagram.

// CASE:-1
let promiseFirst = new Promise(function(resolve,reject){
// after 0.5 second signal that the job is done with the result "Story of novel"
setTimeout(() => resolve("Story of novel"), 500);
});
// CASE:-2
let promiseSecond = new Promise(function(resolve,reject){
// after 0.5 second signal that the job is finished with an error "Something wrong"
setTimeout(() => reject(new Error("Something wrong")), 500); }
});

In the above case 1: — The executor is called automatically and immediately (by new Promise). After 0.5 seconds of “processing”, the executor calls resolve("Story of novel") to produce the result. This changes the state of the promise object:

In case 2: — The executor is called automatically and immediately (by new Promise). After 0.5 seconds of “processing”, the executor calls reject(new Error(“Something wrong”) to produce the error object. This changes the state of the promise object:

To summarize, the executor should perform a job (usually something that takes time) and then call resolve or reject to change the state of the corresponding promise object.

A promise that is either resolved or rejected is called “settled”, as opposed to an initially “pending” promise.

***************************NOTE**********************************

  1. There can be only a single result or an error

i.e The executor should call only one resolve or one reject. Any state change is final. The idea is that a job done by the executor may have only one result or an error.

All further calls of resolve and reject are ignored:

let promise = new Promise(function(resolve, reject) { resolve("done"); // execute
reject(new Error("…")); // ignored
setTimeout(() => resolve("…")); // ignored
});

2. resolve/reject expect only one argument (or none) and will ignore additional arguments.

3. We also can call resolve or reject immediately

In practice, an executor usually does something asynchronously and calls resolve/reject after some time, but it doesn’t have to. We also can call resolve or reject immediately, like this:

let promise = new Promise(function(resolve, reject) {
// not taking our time to do the job
resolve(123); // immediately give the result: 123
});

******************************************************************

Now, most of you are wondering that Hey!! where and how we can access that result(error or value)of a promise object? Do we need to do something ??

So since executor(Author) produces the result (“new novel” or “some info”). It’s time for the consumer(Fans) to consume that result.

The properties state and result of the Promise object are internal. We can’t directly access them. We can use the methods .then/.catch/.finally for that.

How to consume the result or state of Promise?

A Promise object serves as a link between the executor (the “producing code” or “Author”) and the consuming functions (the “fans”), which will receive the result or error.

Consuming functions can be registered (subscribed) using methods .then, .catch and .finally.

  1. then
// Syntax
promise.then(
function(result) { /* handle a successful result */ }, function(error) { /* handle an error */ }
);

The first argument of .then is a function that runs when the promise is resolved and receives the result.

The second argument of .then is a function that runs when the promise is rejected and receives the error.

For instance, here’s a reaction to a successfully resolved promise:

let promise = new Promise(function(resolve, reject) {   setTimeout(() => resolve("done!"), 1000); 
});
// resolve runs the first function in .then
promise.then(
result => alert(result), // shows "done!" after 1 second
error => alert(error) // doesn't run
);

And in the case of a rejection, the second one:

let promise = new Promise(function(resolve, reject) {   setTimeout(() => reject(new Error("Whoops!")), 1000); 
});
// reject runs the second function in .then
promise.then(
result => alert(result), // doesn't run
error => alert(error) // shows "Error: Whoops!" after 1 second
);

If we’re interested only in successful completions, then we can provide only one function argument to .then:

let promise = new Promise(resolve => {
setTimeout(() => resolve("done!"), 1000);
});
promise.then(alert); // shows "done!" after 1 second

2. catch

If we’re interested only in errors, then we can use null as the first argument: .then(null, errorHandlingFunction).

Or we can use .catch(errorHandlingFunction), which is exactly the same:

let promise = new Promise((resolve, reject) => { 
setTimeout(() => reject(new Error("Whoops!")), 1000);
});
// .catch(f) is the same as promise.then(null, f) promise.catch(alert); // shows "Error: Whoops!" after 1 second

3. finally

Just like there’s a finally clause in a regular try {...} catch {...}, there’s finally in promises.

The call .finally(f) is similar to .then(f, f) in the sense that f always runs when the promise is settled: be it resolve or reject.

finally is a good handler for performing the cleanup, e.g. stopping our loading indicators, as they are not needed anymore, no matter what the outcome is.

new Promise((resolve, reject) => {   
/* do something that takes time, and then call resolve/reject */
})
// runs when the promise is settled, doesn't matter successfully or not
.finally(() => stop loading indicator)
.then(result => show result, err => show error)

It’s not exactly an alias of then(f,f) though. There are several important differences:

i. A finally handler has no arguments. In finally we don’t know whether the promise is successful or not. That’s all right, as our task is usually to perform “general” finalizing procedures.

ii. A finally handler passes through results and errors to the next handler.

For instance, here the result is passed through finally to then:

new Promise((resolve, reject) => {   
setTimeout(() => resolve("result"), 2000)
})
.finally(() => alert("Promise ready"))
.then(result => alert(result)); // <-- .then handles the result

And here there’s an error in the promise, passed through finally to catch:

new Promise((resolve, reject) => {   
throw new Error("error");
})
.finally(() => alert("Promise ready"))
.catch(err => alert(err)); // <-- .catch handles the error object

That’s very convenient because finally is not meant to process a promise result. So it passes it through.

We’ll talk more about promise chaining and result-passing between handlers in the next article.

iii. Last, but not least, .finally(f) is a more convenient syntax than .then(f, f): no need to duplicate the function f.

Let’s take an example of how we can load scripts from a website through a promise concept to understand all the above terminology in a single example.

function loadScript(src) {
return new Promise(function(resolve, reject) {
let script = document.createElement('script');
script.src = src;
script.onload = () => resolve(script);
script.onerror = () => reject(new Error(`Script load error for ${src}`));
document.head.append(script);
});
}
let promise = loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"); promise.then(
script => alert(`${script.src} is loaded!`),
error => alert(`Error: ${error.message}`)
);
promise.then(script => alert('Another handler...'));

In the above example, we have created a function loadScript inside that, we have implemented the logic of script loading. Since the loading of the script is a kind of asynchronous behavior that means there could be some time will be taken before resolving the script loading or throw some error. W know In javascript every line of code executed one by one synchronously. So to achieve that asynchronous behavior we have implemented the promise concept.

If loading of script resolved after some time it promise will return resolved value and if there will be a failure then it will throw error object. To access or consume that information we have implemented promise.then . In the first case, we implemented two handlers first one for success and another one is for error. In the second case, there is a single handler, that will handle both resolved and error scenarios with the same approach.

That’s it for the basic understanding of JavaScript promise with its terminology. I hope now all of you have the basic and clear idea about the promise!!😊

In the next article, we will discuss about the chaining and other advanced concepts of promise.

If you really liked this article click the clap 👏button below. Also, write your feedback and queries (if any ) in the comment section.

--

--