Understanding Promise by creating your own : Part 2
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.
- How to enable multiple subscription.
- Subscription after resolution.
- 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