preface

I wanted to know how to write promise for a long time, but I was anxious about things around me and didn’t settle down to study it well. Recently, I decided to get the best out of it, so I wrote a simple, handwritten Promise. This is a basic introduction that only implements the core features of promise code. The rest will be left to a later article.

Method of use

Let’s start by thinking about how we usually use promises. It should be something like this:

function handle(resolve,reject){
    setTimeout(resolve(1),1000)// Simulate asynchronous operations
}
new promise(handle)
Copy the code

Promise is passed in as a handler, which takes resolve, reject. Resolve is used to process successful asynchrony, reject is used to process failed asynchrony. Resolve, reject are essentially functions that can be used to call success or failure arguments.

Basic implementation

Based on the above, we can write the following simple big-frame code (pause to see if this is true) :

function diyPromise(fn){
    function resolve(value){
        console.log('Here is the value passed in by my successful function execution:',value)
    }
    function reject(value){
        console.log('Here is the value passed in by my failed function execution:',value)
    }
    fn(resolve,reject)
}
Copy the code

Now, let’s take a closer look.

We all know that promises have three states: Pending, Reject, and resolve.

(Note: Reject is not to be confused with resolve, which is a string constant despite its name)

So we introduce states:

const PENDING = 'PENDING';// It is about to begin
const REJECT = 'REJECT';/ / fail
const RESOLVE = 'RESOLVE';/ / success
Copy the code

We also need to have an internal variable to make sure that our success and failure values are recorded, and we need to make sure that the functions that are passed in and executed don’t give any errors extra things, we need to catch errors. So the code gets here and looks like this:

const PENDING = 'PENDING';// It is about to begin
const REJECT = 'REJECT';/ / fail
const RESOLVE = 'RESOLVE';/ / success
function diyPromise(fn){
    this.successValue;
    this.errorValue;
    this.status = PENDING;/ / the default pending
    function resolve(value){
        // There is a pit, attention!
        if(this.status===PENDING){
        this.status = RESOLVE; / Change statusthis.successValue = value; / Save successful value}}function reject(value){
        if(this.status===PENDING){
        this.staus = REJECT;
        this.errorValue = value; }}try{
    	fn(resolve,reject)
    }
    catch(e){
        reject(e)
    }
}
Copy the code

One thing to be very careful about here is the direction of this. Consider where our resolve and reject functions are executed. Is called from within our asynchronous function, which means that this refers to window (undefined in JS strict mode). So we need a temporary variable to save this.

Then call

So far, the code in our promise is pretty much done. Next, deal with the callbacks to THEN. Let’s first look at the usage code:

function handle(resolve,reject){
    setTimeout(resolve(1),1000)// Simulate asynchronous operations
}
new promise(handle).then(function(res){
	console.log(res)
    return 'I'm the value of the second then, the next place will print me'
},function(err){
    console.log(err)
})(function(res2){
	console.log(res2)    
},function(err2){
    console.log(err2)
})
Copy the code

One thing you can see: then is a chaining call similar to jquery, where the result of the previous function is used as an input parameter to the next function. So that’s easy to solve, remember the whole point of a chain call is to return this every time. By feeling, code:

const PENDING = 'PENDING';// It is about to begin
const REJECT = 'REJECT';/ / fail
const RESOLVE = 'RESOLVE';/ / success
function diyPromise(fn){
    this.successValue;
    this.errorValue;
    this.status = PENDING;/ / the default pending
     let _this = this;
    function resolve(value){
       
        if(_this.status===PENDING){ _this.status = RESOLVE; / Change status _this.successValue = value; / Save successful value}}function reject(value){
      
        if(_this.status===PENDING){ _this.staus = REJECT; _this.errorValue = value; }}try{
    	fn(resolve,reject)
    }
    catch(e){
        reject(e)
    }
}
diyPromise.prototype.then = function (resolveFn,rejectFn){
    // There are pits
    if(this.status === REJECT){
        rejectFn()
    }
    if(this.status === RESOLVE){
        resolveFn()
    }
    return this
}
Copy the code

There seems to be nothing wrong with the above code, but we missed one thing!

Let’s review the use of promises:

1. Pass in a function and execute it immediately inside the constructor.

2. Because it is asynchronous, an asynchronous function inside haven’t call at the moment the resolve/reject tell us whether success or failure?

3. The main thread continues to run, REJECT, and then, and finds that the state is neither REJECT nor RESOLVE. It’s still PENDING! It then returns this!

After looking at the above process, we will find that then is simply running a lonely ah…

So how do we handle pending operations? This is a difficult place, where we have no way of knowing whether it succeeded or failed, just pending all the time.

Well, can we just write it down and wait for it to succeed or fail and then fire the function?

Where are the successes and failures? Looking up, isn’t it right in our Reject and resolve?

“Then” means that there is more than one “then”. Consider using an array to record the functions received by THEN. The final unification is triggered when the result is determined. Isn’t it a bit of publish and subscribe? The code is as follows:

const PENDING = 'PENDING';// It is about to begin
const REJECT = 'REJECT';/ / fail
const RESOLVE = 'RESOLVE';/ / success
function diyPromise(fn){
    this.successValue;
    this.errorValue;
    this.status = PENDING;/ / the default pending
    this.successList = [];// Success queue to be processed
    this.errorList = [];
    let _this = this;
    function resolve(value){
        if(_this.status===PENDING){
        _this.status = RESOLVE;// Change the state
        _this.successValue = value;// Save the successful value
        _this.successList.forEach(fn= >{
            _this.successValue = fn(_this.successValue)// The success function passes in the success value, and the calculated result overwrites the recorded success value for the next time}}})function reject(value){
        if(_this.status===PENDING){
        _this.staus = REJECT;
        _this.errorValue = value;
         _this.errorList.forEach(fn= >{
            _this.errorValue = fn(_this.errorValue)// The failed function passes in the failed value, and the calculated result overwrites the recorded success value for the next time}}})try{
    	fn(resolve,reject)
    }
    catch(e){
        reject(e)
    }
}
diyPromise.prototype.then = function (resolveFn,rejectFn){
    if(this.status === REJECT){
        rejectFn()
    }
    if(this.status === RESOLVE){
        resolveFn()
    }
    if(this.status === PENDING){
        resolveFn && this.successList.push(resolveFn)
		rejectFn && this.errorList.push(rejectFn)
    }
    return this
}
Copy the code
Finally realize

We still have some imperfections here, because each then should return a new promise, and our THEN still returns itself, so we need another twist!

const PENDING = 'PENDING';// It is about to begin
const REJECT = 'REJECT';/ / fail
const RESOLVE = 'RESOLVE';/ / success
function diyPromise(fn){
    this.successValue;
    this.errorValue;
    this.status = PENDING;/ / the default pending
    this.successList = [];// Success queue to be processed
    this.errorList = [];
    let _this = this;
    function resolve(value){
        if(_this.status===PENDING){
        _this.status = RESOLVE;// Change the state
        _this.successValue = value;// Save the successful value
        _this.successList.forEach(fn= >{
            _this.successValue = fn(_this.successValue)// The success function passes in the success value, and the calculated result overwrites the recorded success value for the next time}}})function reject(value){
        if(_this.status===PENDING){
        _this.staus = REJECT;
        _this.errorValue = value;
         _this.errorList.forEach(fn= >{
            _this.errorValue = fn(_this.errorValue)// The failed function passes in the failed value, and the calculated result overwrites the recorded success value for the next time}}})try{
    	fn(resolve,reject)
    }
    catch(e){
        reject(e)
    }
}
diyPromise.prototype.then = function (resolveFn,rejectFn){
    if(this.status === REJECT){
        rejectFn()
    }
    if(this.status === RESOLVE){
        resolveFn()
    }
    if(this.status === PENDING){
        resolveFn && this.successList.push(resolveFn)
        rejectFn && this.errorList.push(rejectFn)
        return this// Because this promise is not finished yet, return itself first
    }
    return new diyPromise((res) = >{// Each time a new promise is returned, and it proceeds in the next event loop
     setTimeout(() = >{
         this.success = resolveFn(this.success)// Then internal success function passed in})})}console.log(new diyPromise((res) = > {
    setTimeout(() = > {
        res(1)},1000)
}).then(res= > {
    console.log(res)
    return 9999
}).then(res= > {
    console.log(res)
    return 9999
}))
Copy the code

At this point, a core promise principle code implementation is complete.