Understanding Promise by creating your own : Part 2

Pankaj Bhageria
JavaScript Bytes
Published in
3 min readOct 5, 2018

In the Part 1(https://medium.com/javascript-bytes/understanding-promise-by-creating-your-own-part-1-d98d47720bbc) we saw how to implement a basic promise. In this part we will learn few more things about promise

1)Multiple subscription

We saw how to attach a handler when a promise resolves. In our implementation till now, we can attach only single handler. But we should be able to attach multiple handlers to a promise. And all handlers will be called when the promise resolves.

Below example would help you understand.

let p1 = new Promise(function(resolve,reject){
setTimeout(resolve(10),100);
});
p1.then(function(result){
console.log(1,result);
})
p1.then(function(result){
console.log(2,result);
})
p1.then(function(result){
console.log(3,result);
});
//output
1 10
2 10
3 10

Lets modify this code to add this ability. For this we would need to have ability to store multiple listeners

function MyPromise(executor){
this.state = 'initial';
this.value = undefined;
this.onValue = [];
this.onError = [];
executor(resolve.bind(this),reject.bind(this));
}
MyPromise.prototype = {
then: function(handler){
this.onValue.push(handler);
return this;
},
catch:function(handler){
this.onError.push(handler);
return this;
}
}
//please not that below are not global function but private inside //our file
function resolve(value){
this.state = 'resolved';
this.value = value;
this.onValue.map(handler=>handler(value));
}
function reject(error){
this.state = 'rejected';
this.error = error;
this.onError.map(handler => handler(error));
}

So it was quite simple. We are storing an array of listeners instead of a single listener.

So now MyPromise has the ability to handler multiple try catch scenario.

2) Subscription after resolution/rejection.

What happens if someone subscribes to a promise object after it has been resolved/rejected. Lets see

let p1 = new Promise(function(resolve,reject){
setTimeout(resolve(10),100);
});
p1.then(function(result){
console.log(1,result);
})
setTimeout(function(){
p1.then(function(result){
console.log(2,result);
});
},500);
//output
1, 10 // after 100ms
2, 10 //after 500ms

Lets see how to implement the above behaviour. What we need to do is when then/catch is called, if the promise is already resolved or rejected then call the handler immediately.

function MyPromise(executor){
this.state = 'initial';
this.value = undefined;
this.onValue = [];
this.onError = [];
executor(resolve.bind(this),reject.bind(this));
}
MyPromise.prototype = {
then: function(handler){
if(this.state === 'initial'){
this.onValue.push(handler);
}
else if(this.state === 'resolved'){
handler(this.value);
}
else{
//state is rejected, don't do anything
}
return this; },
catch:function(handler){
if(this.state === 'initial'){
this.onError.push(handler);
}
else if(this.state === 'rejected'){
handler(this.error);
}
else{
//promise is resolved do nothing
}
return this;
}
}
//please not that below are not global function but private inside //our file
function resolve(value){
this.state = 'resolved';
this.value = value;
this.onValue.map(handler=>handler(value));
}
function reject(error){
this.state = 'rejected';
this.error = error;
this.onError.map(handler => handler(error));
}

So now our MyPromise can be subscribed to after it is resolved/rejected

3) Asynchronous evaluation(later)

A promise is always resolved asynchronously even if it is resolved synchronously.

let p1 = new Promise(function(resolve,reject){
resolve(1);
});
p1.then(function(result){
console.log(result);//10
});
console.log(2);
//output
2
1

To achieve this in our implementation, we will use setImmediate whenever we are calling our subscribed handlers.

function MyPromise(executor){
this.state = 'initial';
this.value = undefined;
this.onValue = [];
this.onError = [];
executor(resolve.bind(this),reject.bind(this));
}
MyPromise.prototype = {
then: function(handler){
if(this.state === 'initial'){
this.onValue.push(handler);
}
else if(this.state === 'resolved'){
sendResponse(handler,this.value));
}
else{
//state is rejected, don't do anything
}
return this;
},
catch:function(handler){
if(this.state === 'initial'){
this.onError.push(handler);
}
else if(this.state === 'rejected'){
sendResponse(handler,this.error);
}
else{
//promise is resolved do nothing
}
return this;
}
}
//please not that below are not global function but private inside //our file
function resolve(value){
this.state = 'resolved';
this.value = value;
this.onValue.map(handler=>sendResponse(handler,value));
}
function reject(error){
this.state = 'rejected';
this.error = error;
this.onError.map(handler => sendResponse(handler,error));
}
sendResponse(handler,response){
setImmediate(function(){
handler(response);
})

ok. So now our MyPromise implementation is also asynchronous.

So in this post we saw 3 things.

  1. How to enable multiple subscription.
  2. Subscription after resolution.
  3. Making our promise asynchronous so that even when resolve synchronously it will still be async.

We still need to looking into a very important aspect Promise chaining, which we have implemented to some extent. In next part we will cover the same in detail.

Next Part: https://medium.com/javascript-bytes/understanding-promise-by-creating-your-own-part-3-dee0cc41412d

--

--

Pankaj Bhageria
JavaScript Bytes

programmer, meditator, likes to teach and help,believes in keeping things simple