JavaScript Promise, Make Your Own Version in Four Easy Steps
Learn all about Promise by creating one for yourself!

When you are new to web application development, one of the confusing aspects is the asynchronous nature of programming. And when it comes to asynchronous programming in JavaScript, we need to talk about Promise.
Promise was introduced in JavaScript as part of ES6 in 2015, so it is nothing new. However, here we will try to build a replica of the out-of-the-box Promise to understand the inner working of the original one. Let’s call this replica — BasicPromise.
We know from the Promise syntax that,
It has a constructor that takes a callback function with two parameters — resolve and reject, which are in turn functions. One or the other will be called based on whether the operation is a success or a failure.
Promise object stores the result and its state — pending, fulfilled, rejected internally.
We can call a then(), catch() and finally() on the Promise object. These methods also support chaining.
With this information let us try to build our BasicPromise.
1. Define the constructor
The creator of the BasicPromise is expected to write the mainCallback() function and pass it to the constructor. The constructor calls this callback function immediately with resolve and reject as parameters. It is also wrapped in a try..catch block so that if any error is thrown, BasicPromise will be rejected inside the catch(). Simple isn’t it?
The creator can now decide to call the resolve() or reject() at a later point in time — in other words, asynchronously. Let’s write a callback function that waits for a second and checks the time in milliseconds. It then calls resolve() if the value is even or calls reject() if it is odd.
However, there is one small problem. Our current BasicPromise implementation completely ignores the value passed into the resolve() or reject() function. We need to fix this!
2. Result and state management
We ignored two main aspects of Promise — its ability to store the result and the state. Promise object will get its result when the creator either calls resolve() or reject() with a value. Based on the above example we get — ‘even’ or ‘odd’.
State of a Promise starts with ‘pending’ and goes to either ‘fulfilled’ or ‘rejected’ based on the function that is called — resolve() or reject().
Let's enhance the BasicPromise to store the result and state.
Note that the state or result cannot be modified once it is ‘fulfilled’ or ‘rejected’. So the Promise is said to be ‘settled’ when it reaches one of these end states.
3. Handle method calls
The consumer of the Promise object can call then(), catch(), and finally() methods with callback functions. These methods also support Promise chaining. It means each of these methods should return a new Promise.
Let’s try to enhance our BasicPromise further.
The new BasicPromise created inside then(), catch(), and finally() will be fulfilled or rejected based on the previous BasicPromise. However, at the time of calling these methods — then(), catch(), and finally(), BasicPromise may not have been fulfilled. In other words, results won’t available. So we push the callbacks to a callbackList and invoke it as soon as the BasicPromise is either fulfilled or rejected.
Sometimes, BasicPromise may already be ‘fulfilled’ or ‘rejected’ when then(), catch(), and finally() are called. In such cases, newly returned BasicPromise can be immediately fulfilled/rejected with the result stored in the previous BasicPromise object.
Another point to note is every callback invocation is wrapped in a try..catch block and inside the catch() we reject the BasicPromise. This is true even with the callback of finally(). When an error is thrown in the callback of finally() it goes to the catch() block and is rejected.
Also, notice that whenever a rejectCallback() is invoked, the next Promise is fulfilled, and not rejected, with the return value of the callback function. In other words, catch() will always handle the ‘rejected’ Promise and return a ‘fulfilled’ Promise, unless an error is thrown inside the rejectCallback() itself. In such cases, when an error is thrown, it will return a rejected Promise.
4. Consume the Promise
The BasicPromise object we created at the beginning can now be consumed by calling then(), catch(), and finally().
As you can see, we have chained the calls since then(), catch() and finally() each returns a BasicPromise that gets fulfilled or rejected based on the previous BasicPromise.
BasicPromise was created in an attempt to better understand the inner workings of the actual Promise in JavaScript. Hope it served its purpose.
As the name suggests, the focus was only on the core functionality, and naturally, it misses additional bells and whistles of actual Promise. For e.g. we have not covered the static methods of Promise — all(), allSettled(), any(), race(), resolve(), reject(). There could be other features that got missed out intentionally or otherwise. Hence, it is not intended as a replacement or as an alternative to the native Promise.
Originally published at https://medium.com