Promise implementation principle

We’ve used promises countless times in our coding, so how do promises come true?

Today we are going to implement a better promise step by step.

I believe that some students already know the principle, so you can review the old and learn the new, we can study together.

1 Implementation of basic promises

A promise has three states: Pending Resolved Rejected. The promise can be switched from the Pending state to Resolved and Rejected only once, and then the final state will remain.

Let’s look at a basic promise in action.

new Promise(function(resolve,reject){
    resolve(123)
    // reject(456)
}).then(function(data){
    console.log('success',data) // success 123
},function(reason){
    console.log('fail',reason)
})
Copy the code

Call the method that passes in a Promise an executor, which executes immediately when a Promise is constructed. The parameters of the then method are passed in. The first ondepressing function is the parameter of Promise resolve, which will be executed after the Promise state is resolved; The second onRejected function takes the parameter of Promise Reject and is executed after the Promise state is Rejected.

Parameters like 123 or 456 become the final value of a promise.

From this we implement a basic promise

function Promise (executor){
    const self = this
    this.state = 'pending'
    this.value = undefined

    this.resolveCallback = undefined
    this.rejectCallback = undefined

    function resolve(data){
        if(self.state === 'pending'){
            self.state = 'resolved'
            self.value = data
            if(self.resolveCallback){
                self.resolveCallback(data)
            }
        }
    }

    function reject (reason){
        if(self.state === 'pending'){
            self.state = 'rejected'
            self.value = reason
            if(self.rejectCallback){
                self.rejectCallback(reason)
            }
        }
    }

    executor(resolve,reject)
}

Promise.prototype.then = function (onFulfilled,onRejected){
    if(this.state === 'pending'){
        this.resolveCallback = onFulfilled
        this.rejectCallback  = onRejected
    }

    if(this.state === 'resolved'){
        onFulfilled(this.value)
    }

    if(this.state === 'rejected'){
        onRejected(this.value)
    }
}
Copy the code

Executor contains asynchronous functions

Then is a pending state

new Promise(function(resolve,reject){
    setTimeout(function(){
        resolve(123)
    },0)
}).then(function(data){
    console.log('success',data)
},function(reason){
    console.log('fail',reason)
})
Copy the code

The executor function throws error

try{
    executor(resolve,reject)
}catch(e){
    reject(e)
}
Copy the code

4 Register multiple THEN

Callbacks are placed in arrays

functionPromise (executor){// omit this.resolveCallback = [] this.rejectCallback = [] // omitfunction resolve(data){
    if(self.state === 'pending'){
        self.state = 'resolved'
        self.value = data
        self.resolveCallback.forEach(fn => {
            fn(data)
        })
    }
}

function reject (reason){
    if(self.state === 'pending'){
        self.state = 'rejected'The self. The value = "reason self. RejectCallback. ForEach (fn = > {fn (reason)}) / / omit}}}Copy the code

5 Chain call

Do not return the same promise

const promise1 = new Promise(function(resolve,reject){
    setTimeout(function(){
        resolve(123)
    },0)
    
})
const promise2 = promise1.then(function (data){
    return456},function(reason){
    console.log('reason',reason)
})
console.log('compare',promise1 == promise2) // false
Copy the code

So instead of returning the current this, the then function returns a new promise

promise1.then(function(data){
    console.log('success',data)
    return456},function(reason){
    console.log('fail',reason)
}).then(function(data){
    console.log('success1',data)
},function(reason){
    console.log('fail1',reason)
})
Copy the code
Promise.prototype.then = function (onFulfilled,onRejected){
    var self = this
    if(this.state === 'pending') {return new Promise(function(resolve,reject){

            self.resolveCallback.push(function(){
                var x = onFulfilled(self.value)
                resolve(x)
            })
            self.rejectCallback.push(function(){
                var x = onRejected(self.value)
                reject(x)
            })
        })
        
    }

    if(this.state === 'resolved') {return new Promise(function(resolve,reject){
            var x = onFulfilled(self.value)
            resolve(x)
        })
        
    }

    if(this.state === 'rejected') {return new Promise(function(resolve,reject){
            var x = onRejected(self.value)
            reject(x)
        })
    }
}

const promise1 = new Promise(function(resolve,reject){
    setTimeout(function(){
        resolve(123)
    },0)
    
})
Copy the code

6 Returns a promise when a chained call is made

const promise1 = new Promise(function(resolve,reject){
    setTimeout(function(){
        resolve(123)
    },0)
    
})

promise1.then(function(data){
    console.log('success',data)
    return new Promise(function(resolve,reject){
        resolve(456)
    })
},function(reason){
    console.log('fail',reason)
}).then(function(data){
    console.log('success1',data)
},function(reason){
    console.log('fail1',reason)
})
Copy the code
Promise.prototype.then = function (onFulfilled,onRejected){
    var self = this
    var promise2
    if(this.state === 'pending'){
        promise2 = new Promise(function(resolve,reject){
            
            self.resolveCallback.push(function(){
                try{
                    var x = onFulfilled(self.value)
                    resolvePromise(promise2,x,resolve,reject)
                }catch(e){
                    reject(e)
                }
            })
            self.rejectCallback.push(function(){
                try{
                    var x = onRejected(self.value)
                    resolvePromise(promise2,x,resolve,reject)
                }catch(e){
                    reject(e)
                }
            })
        })
        
    }

    if(this.state === 'resolved'){
        promise2 =  new Promise(function(resolve,reject){ try{ var x = onFulfilled(self.value) resolvePromise(promise2,x,resolve,reject) }catch(e){ reject(e) } })}if(this.state === 'rejected'){
        promise2 =  new Promise(function(resolve,reject){ try{ var x = onRejected(self.value) resolvePromise(promise2,x,resolve,reject) }catch(e){ reject(e) } })}return promise2
}
function resolvePromise(promise,x,resolve,reject){
    if(promise === x){
        throw new Error('Promise loop reference')}if(x instanceof Promise){
        try{
            x.then(function(data){
                resolve(data)
            },function(reason){
                reject(reason)
            })
        }catch(e){
            reject(e)
        }
        

    }else{
        resolve(x)
    }
} 
Copy the code

7 through

const promise1 = new Promise(function(resolve,reject){
    setTimeout(function(){
        // resolve(123)
        reject(123)
    },0)
})

promise1.then().then()
.then(function(data){
    console.log('success1',data)
},function(reason){
    console.log('fail1',reason) // fail1 123
})
Copy the code

The solution

Promise.prototype.then = function(this is a big pity, onFulfilled){// omitted onFulfilled === typeof onFulfilled === this is a big pityfunction ?  onFulfilled : function(data){return data}
    onRejected = typeof onRejected === function ?  onRejected : function(data){returnData} // omit}Copy the code

8 Asynchronous Execution

Refer to the Promise A+ specification:

conclusion

Promise to realize the core is then processed, other methods promise. All, promise. Race, promise. Reject, promise. Resolve, promise. After then clear catch would be easy to write.

The source address

discuss

How might our Promise implementation be different from the Promise implementation built into the browser?

The resources

Juejin. Cn/post / 684490… www.ituring.com.cn/article/665… Stackoverflow.com/questions/5…