Realization of promise source code

Time:2021-5-11

Promise

1、 Simple use of promise

new Promise((resolve, reject) => {
    resolve("ok")
}).then(data => {
    console.log(data) //OK
}, error => {
    console.log(error)
})
//Simplify
let promise = new Promise(executor)
promise.then(onFulfilled, onRejected)

It should be noted that

  • executorIs a callback function that will be executed immediately with two parameters,resolveand reject
  • IfexecutorCalled inresolve(value)So thisvalueWill be passed on toonFulfilledAs in the first exampleconsole.log(data)The output isok
  • IfexecutorCalled inreject(value)So thisvalueWill be passed on toonRejected, asonRejectedThe actual parameters of
  • Another thing to note is that promise has a state, and it defaults to pending at the beginning. When callingresolveThe status changes tofulFilled, when callingrejectThe status changes torejected. When the state changes frompendingChange tofulFilled / rejectAfter that, the state can not be changed

2、 Primary implementation of promise

Let’s continue with the first use case to do a preliminary implementation of promise

new Promise((resolve, reject) => {
    resolve("ok")
}).then(data => {
    console.log(data) //OK
}, error => {
    console.log(error)
})
//We implement it line by line with examples
// let executor = (resolve, reject) => { resolve("ok") }
// new Promise(executor)
class Promise {
    constructor(executor){
        This.status = pending // the default status is waiting
        //executor = (resolve, reject) => { resolve("ok") }
        //The passed executor is a function with two parameters,
        //The parameters resolve and reject are callable functions
        //So we should first declare these two functions and then pass them into the executor as arguments
        const resolve = (value) => {
            
        }
        const reject = (error) => {
            
        }
        executor(resolve,reject)
    }
    
}
// let executor = (resolve, reject) => { resolve("ok") }
// new Promise(executor)
class Promise {
    constructor(executor){
        this.status = "penging"
        this.resValue = null
        this.rejValue = null
        // executor = (resolve, reject) => { resolve("ok") }
        const resolve = (value) => {
            //Such as "OK" in resolve ("OK") 
            //You need to get this value in the onfulled of then in the following code, so you need to store this value 
            this.resValue = value
            //After calling resolve, the state changes to full
            if(this.status === "pending"){
                this.status = "fulfilled"
            }
        }
        const reject = (error) => {
            this.rejValue = error
            if(this.status === "pending"){
                this.status = "rejected"
            }
        }
        executor(resolve,reject)
    }
    
}
// let promise = new Promise(executor)
// promise.then(onFulfilled, onRejected)
//We can conclude that promise needs a then method
class Promise {
    constructor(executor){
        //As above
    }
    then(onFulfilled, onRejected){
        //When calling the then method, you should judge whether the current state is successful or failed
        if(this.status === "fulfilled"){
            //If the current status is success
            onFulfilled(this.value)
        }
        if(this.status === "rejected"){
            //If the current status is failed
            onRejected(this.error)
        }
    }
}

This basically completes the primary function of promisenew promise(executor)IncomingexecutorWhen it’s an asynchronous function, there’s a problem. As follows:

new Promise((resolve,reject)=>{
    setTimeout(()=>{
       resolve("ok") 
    },0)
}).then(data => {
    Console. Log (data) // will not output OK
})
//The reason is: when the executor passes in new promise 'for an asynchronous function
//Although the executor is called immediately, it is an asynchronous function and will be pushed into the micro task and will not be executed immediately
//Therefore, the following resolve will not be executed immediately, but the then method of the macro task will be executed first
//So when the then method is executed, the promise state is still pending
class Promise {
    constructor(executor){
        // executor = setTimeout(()=>{resolve("ok")},0)
        const resolve = (value) => {
            //* * 2. Post implementation
            this.value = value
            if(status === "pending"){
                this.status = "fulfilled"
            }
        }
        // ...
        executor(resolve,reject)
    }
    then(onFulfilled, onRejected){
        //* * 1. Execute first
        if(this.status === "fulfilled"){
            onFulfilled(this.value)
        }
        // ...
    } 
}

Asynchronous improvement of primary implementation of promise

Now the problem is that when we pass in an asynchronous function, we execute tothenMethod, the state is stillpending, so cannot execute toonFulfilled. We need to be in theexecutorCalled inresolvePost implementationonFulfilled. So it’s natural to think of using publish subscribe mode to solve this problem. That is, inthenWhen the current waiting state is determined in theexecutorIt must be an asynchronous function)onFulfilledandonRejectedSave it, save itexecutorExecute after resolveonFulfilled. The following is the code implementation:

class Promise {
    constructor(executor){
        this.status = "pending"
        //Onfulfilled set
        this.onFulfilledCallbacks = [ ]
        //Onrejected set
        this.onRejectedCallbacks = [ ]
        
        const resolve = (value) => {
            this.resValue = value
            if(status === "pending"){
                this.status = "fulfilled"
                //Release
                this.onFulfilledCallbacks.forEach(callback => {
                    callback(this.value)
                })
            }
        }
        const reject = (error) => {
            this.rejValue = error
            if(status === "pending"){
                this.status = "rejected"
                this.onRejectedCallbacks.forEach(callback => {
                    callback(this.error)
                })
            }
        }
        executor(resolve,reject)
    }
    then(onFulfilled, onRejected){
        //Synchronous code unchanged, add asynchronous code
        if(this.status === "fulfilled"){
            onFulfilled(this.value)
        }
        if(this.status === "rejected"){
            onRejected(this.error)
        }
        //New code
        if(this.status === "pending"){
            //It's then, and the status hasn't changed.
            //It indicates that the function in the executor is asynchronous
            //Subscription
            this.onFulfilledCallbacks.push((data)=>{
                //Slice
                onFulfilled(data)
            })
            this.onRejectedCallbacks.push((error)=>{
                onRejected(error)
            })
        }
        
    }
}

The above is the primary implementation of promise, and the core of promise is their chain call. We’ll go into details in the next chapter

3、 Understand the chain call of promise

    new Promise((resolve,reject) => {
        resolve("hello")
    }).then(data => {
        console.log(data) //hello
        return "world"
    }).then(data => {
        console.log(data) //world
    })

If in thenreturnA simple value (notpromise)In the next thenonFulfilledTake this value.

    new Promise((resolve,reject) => {
        resolve("hello")
    }).then(data => {
        throw new Error()
    }).then(data => {
        console.log(data) 
    },error => {
        console.log(error) // Error
    })

If an exception is thrown in then, it will be thrown in the next thenonRejectedGet this exception.

let promise1 = new Promise((resolve,reject) => {
        resolve("hello")
    }).then(data => {
        return data
    })
    
    console.log(promise1) // Promise{<fulfilled>: "hello"}
    //You can see that in the then method, a promise object is returned
    //Because the state of promise cannot be changed twice, a new promise object should be returned

4、 Test case analysis

Before completing the chain call of promise, there must be a test case

let promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("the message is ")
    }, 0);
})
let promise2 = promise1.then(data => {
    let interPromise = new Promise((resolve,reject) => {
        setTimeout(() => {
            resolve(data + "that today is a happy day")
        }, 0);
    })
    return interPromise
}, null)

I think it’s necessary to analyze the promise 2 first

  • First of all, we know thispromise1.then()It must be a new onepromiseAnd it’s a complete or ending statepromise
  • 2. So inthenThe internal generation of apromiseAnd did it internallypromiseOfresolve(data)And thisdataFrom insideinterPromiseOfresolve()
  • 3. Take another look at thisinterPromiseHe wants to put the internalresolve(data + "that today is a happy day")The data in the database is transmitted to the upper layerthenOfpromise, then it can only be an implicit callinterPromiseOfthenAnd in thisthenCall the upper layerpromiseOfresolve
  • This ispromiseIf you don’t understand it, you can read it several times. If you don’t understand it, you can see the following code implementation first, and then look back at the analysis
let promise3 = promise2.then(data => {
    console.log(data) // expect: the message is that today is a happy day
})

6、 Chain call of promise

In order to facilitate reading and understanding, this case implements the most difficult function, and omits exception capture and other contents.

//The constructor remains unchanged, only the then method is modified
class Promise {
    constructor(executor) {
        this.status = "pending"
        this.resValue = null
        this.rejValue = null
        this.onfulfilledCallbacks = []
        this.onrejectedCallbacks = []
        const resolve = (value) => {
            this.resValue = value
            if (this.status === "pending") {
                this.status = "fulfilled"
                this.onfulfilledCallbacks.forEach(callback => {
                    callback(this.resValue)
                })
            }
        }
        const reject = (error) => {
            this.rejValue = value
            if (this.status === "pending") {
                this.status = "rejected"
                this.onrejectedCallbacks.forEach(callback => {
                    callback(this.rejValue)
                })
            }
        }
        executor(resolve, reject)
    }
    then(onFulfilled, onRejected) {
        // onFulfilled = data => new Promise(...)
        //The then method returns promise 2
        const promise2 = new Promise( (resolve2,reject2) => {
            if (this.status === "pending") {
                //async 
                //Here we need an asynchronous implementation
                //Because in resolvepromise, you need to complete the initialization of promise 2
                setTimeout(()=>{
                    this.onfulfilledCallbacks.push(data => {
                        //From the perspective of cases
                        //This x = interpromise
                        let x = onFulfilled(data)
                        //Then judge whether x is a promise
                        resolvePromise(x, resolve2, reject2)
                    })
                    
                    this.onrejectedCallbacks.push(error => {
                        //Make changes by analogy, but not here
                        onRejected(error)
                    }) 
                },0)
            }
            //The following is the same as the above. I will not make any changes
            if (this.status === "fulfilled") {
                onFulfilled(this.resValue)
            }
            if (this.status === "rejected") {
                onRejected(this.rejValue)
            }
            
        })
        return promise2
    }
}

function resolvePromise(x, resolve2, reject2){
    //There are many exceptions in the source code. Here is the core code
    if((typeof x === "object" && typeof x !== "null")|| typeof x === "function"){
        // x is a object or function
        let then = x.then
        if(typeof then === "function"){
            //Call then of interpromise here 
            //And call the resolve of the upper promise
            then.call(x,data => resolve2(data),null)
            // x is a promise
            // for example: x = new promise((resolve,reject) => {resolve("ok")})
        }
    }
}

Recommended Today

Looking for frustration 1.0

I believe you have a basic understanding of trust in yesterday’s article. Today we will give a complete introduction to trust. Why choose rust It’s a language that gives everyone the ability to build reliable and efficient software. You can’t write unsafe code here (unsafe block is not in the scope of discussion). Most of […]