js: Promise source code implementation (function, prototype)

Reference : https://juejin.cn/post/7047492676190634021#comment
https://juejin.cn/post/7102708871763853348

1. Prerequisite knowledge:

(1) Basic knowledge of promises

  • Role: asynchronous processing; solve "callback hell" and improve readability
  • Three states: pending, resolved (also called fulfilled), rejected (the latter two states can only be converted from pending, there is no mutual conversion between resolved and rejected)

Instance method:

  • then: a promise object will be returned; the parameters are two callback functions, the execution functions corresponding to the resolved and rejected states
  • catch: equivalent to the second function in then
  • all: an array of promise objects, if all execution is successful, return a promise object whose status is resolved; otherwise rejected
  • race: an array of promise objects, returns the first fulfilled promise object, whether it is resolved or rejected

(2) Prototype

Write methods on the prototype of the MyPromise class, instance p can inherit

2. Promise realization

(1) Constructor

function MyPromise(executor) {
    
    
    this.status = 'pending'
    this.result = null

    // 箭头函数不会改变this指向,this仍然指向当前的promise对象,而不是指向resolve函数
    const resolve = (data) => {
    
    
        if(this.status !== 'pending')
            return
        
        this.status = 'resolved'
        this.result = data
        
        // 队列思想,先进先出;
        // 1、执行then方法时,为pending状态的每个promise对象保存一对回调函数;
        // 2、等到状态变为resolved时,从队列中取出暂存的回调函数,顺序调用
        while(this.onResolvedCallbacks.length > 0) {
    
    
            this.onResolvedCallbacks.shift()()  // 调用
            this.onRejectedCallbacks.shift()
        }
    }

    const reject = (data) => {
    
    
        if(this.status !== 'pending')
            return
        
        this.status = 'rejected'
        this.result = data
        
        while(this.onRejectedCallbacks.length > 0) {
    
    
            this.onResolvedCallbacks.shift()
            this.onRejectedCallbacks.shift()()  // 调用
        }
    }

    // 把写好的resolve、reject作为MyPromise构造函数的两个参数
    executor(resolve, reject)
}

Understanding of executor (resolve, reject):

① First look at the creation form of the promise object:

// 箭头函数就对应了上面代码中的executor
let p1 = new MyPromise((resolve, rejct)=>{
    
    
	// 函数体
	// 需要显式调用resolve、reject函数,修改promise状态
	resolve('ok')
})

② The two functions resolve and reject
are written in advance in MyPromise, using executor(resolve, reject), and assigned to the two corresponding parameters of the MyPromise constructor

Changing to the following form also has the same effect, because here is the formal parameter:

let p1 = new MyPromise((success, failed)=>{  success('ok') })

Then, when called in the function body, the corresponding function code will be executed

③ The role of executor
Corresponds the parameter name (callback function name) of the instance with resolve and reject in the MyPromise class

(2) then, catch method

① then returns a promise object (supports chain calls)

MyPromise.prototype.then = function (onResolved, onRejected) {
    
    
    // 如果传入参数不是函数,为其初始化为空函数
    onResolved = typeof onResolved === 'function' ? onResolved : ()=>{
    
    }
    onRejected = typeof onRejected === 'function' ? onRejected : ()=>{
    
    }

    return new MyPromise((resolve, reject) => {
    
    
        // this指向执行then方法的MyPromise实例
        if(this.status === 'resolved') {
    
    
            let res = onResolved(this.result)   // then方法的返回值
            resolve(res)    // 传递给下一个then
        }
        if(this.status === 'rejected' && onRejected) {
    
    
            let err = onRejected(this.result)
            reject(err)
        }
        // 说明前一步还没有完成,先保存两个回调,等待完成后再执行
        if(this.status === 'pending') {
    
    
            this.onResolvedCallbacks.push(()=>{
    
    
                let res = onResolved(this.result)
                resolve(res)
            })
            this.onRejectedCallbacks.push(()=>{
    
    
                let err = onRejected(this.result)
                reject(err)
            })
        }
    })
}

② catch is equivalent to the then method whose second parameter is an empty function

MyPromise.prototype.catch = function(onRejected) {
    
    
    if(this.status === 'rejected') {
    
    
        onRejected(this.result)
    }
    if(this.status === 'pending') {
    
    
        this.onResolvedCallbacks.push(()=>{
    
    
            // push一个空的回调函数,占位
        })
        this.onRejectedCallbacks.push(()=>{
    
    
            onRejected(this.result)
        })
    }
}

③ Chain call then method:

let p1 = new MyPromise((resolve, reject)=>{
    
    
	// resolve('ok')
    reject('error')
})
p1.then((value)=>{
    
    
    console.log('value', value)
    return value
}
,(reason)=>{
    
    
    console.log('reason:', reason)	// 执行,输出 reason:error
    return reason
}).then((res)=>{
    
    
    console.log('res:', res)
},(err)=>{
    
    
    console.log('err:', err)	// 执行,输出 err:error
    return 'catch_Error'
}).catch((err)=>{
    
    
    console.log('catch:', err)	// 执行,输出 catch:catch_Error
})

(3) all and race methods

① all: an array of promise objects, only when all objects are in the resolved state, return a resolved promise object (composed of the return value of the original array); otherwise return the value of the first rejected promise object

MyPromise.prototype.all = function(promiseArray) {
    
    
    const values = []
    let count = 0

    return new MyPromise((resolve, reject)=>{
    
    
        promiseArray.forEach((p, index) => {
    
    
            // 把p包装成MyPromise对象,因为可能传入的p并不是promise对象
            p = p instanceof MyPromise ? p : new MyPromise((resolve, reject) => resolve(p))
            p.then(
                (value)=>{
    
    
                    count++
                    values[index] = value   // 保持跟原数组顺序一致
                    if(count === promiseArray.length) {
    
    
                        resolve(values)
                        console.log('status:', this.status)
                    }
                },
                (err) => {
    
    
                    reject(err)
                }
            )
        })
    })
}

② race: an array of promise objects, returning the first completed promise object, whether successful or not

MyPromise.prototype.race = function(promiseArray) {
    
    
    return new MyPromise((resolve, reject)=>{
    
    
        promiseArray.forEach((p) => {
    
    
            p = p instanceof MyPromise ? p : new MyPromise((resolve, reject) => {
    
     resolve(p) })
            p.then(
                (value)=>{
    
    
                    resolve(value)
                },
                (err) => {
    
    
                    reject(err)
                }
            )
        })
    })
}

Call all and race methods:

let p1 = new MyPromise((resolve, reject)=>{
    
    
    resolve('p1')
})
let p2 = new MyPromise((resolve, reject)=>{
    
    
    resolve('p2')
})
let p3 = new MyPromise((resolve, reject)=>{
    
    
    resolve('p3')
})
let p4 = new MyPromise((resolve, reject)=>{
    
    
    reject('p4')
})

let P1 = new MyPromise((resolve, reject)=>{
    
    })

P1.all([p1,p2,p3]).then((res)=>{
    
    
    console.log('res:', res)
}, (err)=>{
    
    
    console.log('err:', err)

})

P1.all([p1,p2,p4]).then((res)=>{
    
    
    console.log('res:', res)
}, (err)=>{
    
    
    console.log('err:', err)

})

P1.all(['1','2','3']).then((res)=>{
    
    
    console.log('res:', res)
}, (err)=>{
    
    
    console.log('err:', err)

})

P1.race([p1,p2,p4]).then((res)=>{
    
    
    console.log('res:', res)
}, (err)=>{
    
    
    console.log('err:', err)

})

P1.race([p4,p1,p2]).then((res)=>{
    
    
    console.log('res:', res)
}, (err)=>{
    
    
    console.log('err:', err)

})

insert image description here

3. Supplement

The difference between the catch method and the second function in the then method?
——catch is equivalent to the second method of then, but refers to the next then, not the previous then
——In the same then, the second method cannot catch the exception thrown by the first method

let p = new Promise((resolve, reject)=>{
    
    
	
})
p.then(()=>{
    
    }, ()=>{
    
    })
	.catch(()=>{
    
    })
p.then(()=>{
    
    },()=>{
    
    })
	.then(()=>{
    
    }, ()=>{
    
     
		// 相当于这个方法 
	})

Guess you like

Origin blog.csdn.net/qq_38432089/article/details/127005766