0


手写Promise的基本实现 (超详细)

//本文带大家实现一个基本的 promise 过多的边界情况就不在考虐,理解主要实现过程以及逻辑即可

//对于一个个出现的问题 我会逐步分析原因以及对应的解决思路

//前提是你必须掌握 promise的基本使用,以及回调函数有对应的理解

//第一步:初步实现一个最简单的Promise (循序渐进的方式往下编写,小白也能听懂,当然大神可直接跳过一些废话~~~ )

一:首先分析官方的promise

//---首先分析官方的promise

const p1 = new Promise((resolve, reject) => {
    resolve(2222)
    reject(33333)
})
// 调用then方法 then 方法接收2个回调函数作为参数
p1.then(res => { //只会打印222, 因为promise的状态一但由pending的状态改变了 就无法再次改变
    console.log("res1:", res)
}, err => {
    console.log("err:", err)
})

//解析:

//由此可见 promise 的参数 接收一个函数,在函数中又接收了2个回调函数 作为参数,然后调用resolve 方法

//在执行then方法。 then 函数又接收2个参数 均为回调函数,当执行 resolve(2222)后 就会来到then 的第一个参数 的回调函数

// 执行 reject(33333)后 就会来到then 的第二个参数回调函数-

//1.那么初步编写可得:

const PROMISE_STATUS_PENDING = 'pending' //定义三种状态常量
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'

class myPromise {
    constructor(executor) {
        this.status = PROMISE_STATUS_PENDING //1.初始化状态 pending
        this.value = undefined //2.保存参数
        this.error = undefined
        const resolve = ((value) => {
            if (this.status === PROMISE_STATUS_PENDING) { //3. 只有pending状态才可改变
                this.status = PROMISE_STATUS_FULFILLED
            }
        })

        const reject = ((error) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                this.status = PROMISE_STATUS_REJECTED

            }
        })

        executor(resolve, reject) //在new myPromise 时初始化立即调用传递过来的函数
    }
}

二:手写Promise-then方法设计

//2. 执行resolve 或者 reject 会调用then 方法(在上面的代码基础改动)

class myPromise {
    constructor(executor) {

        this.status = PROMISE_STATUS_PENDING
        this.value = undefined
        this.error = undefined
        const resolve = ((value) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                this.status = PROMISE_STATUS_FULFILLED

                this.resfn(value)
            }
        })

        const reject = ((error) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                this.status = PROMISE_STATUS_REJECTED

                this.errfn(error)
            }
        })

        executor(resolve, reject)
    }

    then(resfn, errfn) { //4.then 方法接收2个回调函数作为 参数 : 第一个为成功的回调,第二个失败的回调
        this.resfn = resfn
        this.errfn = errfn
    }
}

//(看代码是不是觉得最基本的实现了)接下来我们执行下自己实现的promise,看是否正常

const p1 = new myPromise((resolve, reject) => {

resolve(111)

})

p1.then(res => {

console.log(res);

}, err => {

})

//执行后你会发现报错了 错误信息: this.resfn is not a function

//那么为什么会报错呢? 因为和 代码的执行顺序有关 当我们 new myPromise()执行resolve 的时候就会来到resolve方法

//而你的then 方法鸭羹就没有执行到呢 所以就报错了

//解决方法: 只要让then 先执行就可以 , 将resolve 或者 reject 要执行then 方法时添加到异步队列即可

上代码:


class myPromise {
    constructor(executor) {
        this.status = PROMISE_STATUS_PENDING
        this.value = undefined
        this.error = undefined
        const resolve = ((value) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                this.status = PROMISE_STATUS_FULFILLED
                //大家可能会想到使用setTimeout 然后0秒执行,但此处使用queueMicrotask 会更加合适
                // setTimeout(() => {
                //     this.resfn(value)
                // }, 0);

                queueMicrotask(() => { //queueMicrotask:  主线程执行完毕之后立马执行
                    this.resfn(value)
                })
            }
        })

        const reject = ((error) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                this.status = PROMISE_STATUS_REJECTED
                queueMicrotask(() => {
                    this.errfn(error)
                })
            }
        })
        executor(resolve, reject)
    }

    then(resfn, errfn) { //4.then 方法接收2个回调函数作为 参数 : 第一个为成功的回调,第二个失败的回调
        this.resfn = resfn
        this.errfn = errfn
    }
}

//举一个栗子:
setTimeout(() => {
    console.log('setTimeout');
}, 0);

queueMicrotask(() => {
    console.log('queueMicrotask');
});

//实际运行
queueMicrotask
setTimeout

然后执行:

const p1 = new myPromise((resolve, reject) => {

resolve(111)

reject(333333)

})

p1.then(res => { //最终打印 1111

console.log(res);

}, err => {

console.log(err);

})

到这里 我们最简单的实现就以经完成了,接下来我们继续 优化then 方法

优化一:

(官方的Promise 的then是可以多次调用的)

// 我们自己实现的mypromise 调用then方法多次时 我们目前是不支持的

p1.then(res => {

  console.log("res1:", res) 

}, err => {

  console.log("err1:", err) 

})

// 调用then方法多次调用

p1.then(res => {

  console.log("res2:", res) 

}, err => {

  console.log("err2:", err) 

})

运行结果:res2: 111 因为后面的.then 把前面的覆盖掉了 并不会执行res1 所在的代码块

*由此可见 then 方法调用时应该是个数组然后依次调用

下面我们改造我们的代码:

上代码:

class myPromise {
    constructor(executor) {
        this.status = PROMISE_STATUS_PENDING
        this.value = undefined
        this.error = undefined

        this.resfns = [] //1.多次调用then 时用数组 保存
        this.errfns = []

        const resolve = ((value) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                this.status = PROMISE_STATUS_FULFILLED
                queueMicrotask(() => {
                    this.value = value
                    this.resfns.forEach(fn => {        //循环依次调用
                        fn(this.value)
                    })
                })
            }
        })

        const reject = ((error) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                this.status = PROMISE_STATUS_REJECTED
                queueMicrotask(() => {
                    this.error = error
                    this.errfns.forEach(fn => {
                        fn(this.error)
                    })
                })
            }
        })
        executor(resolve, reject)
    }

    then(resfn, errfn) {        //将多次调用的then 方法保存到数组中,依次调用
        this.resfns.push(resfn)
        this.errfns.push(errfn)
    }
}

然后执行:

const p1 = new myPromise((resolve, reject) => {

   resolve(111) 


   reject(333333) 

})

p1.then(res => {

   console.log("res1:", res) 

}, err => {

   console.log("err1:", err) 

})

// 调用then方法多次调用

p1.then(res => {

   console.log("res2:", res) 

}, err => {

   console.log("err2:", err) 

})

执行结果:

res1: 111

res2: 111

这样是不是就解决了 我们多次调用的问题, 那么仔细看我们的代码

我们在 resolve 函数 中 只有他的状态为pending的时候 我们才会执行 then的回调函数

那么如果我们执行then 的时候 他的状态 已经不是pending的时候 那么是不是就不会执行了呢?

那有些朋友可能会问到了:“哎,怎么可能执行then 的时候他的状态 已经被改变了呢 我明明看到你写的代码 ,是把它成功的回调和失败的回调push在了数组里面,然后依次的执行吗”

说的可能比较抽象:接下来我们用代码复现这个问题

const promise = new HYPromise((resolve, reject) => {

 console.log("状态pending") 


 resolve(1111)  


 reject(2222) 

})

// 调用then方法多次调用

promise.then(res => {

 console.log("res1:", res) 

}, err => {

 console.log("err:", err) 

})

promise.then(res => {

 console.log("res2:", res) 

}, err => {

 console.log("err2:", err) 

})

/** 仔细看这里 上面的代码是同步的 执行会被添加到对应的数组里面依次调用,调用完毕后,他的状态是不是就已经确定好了 */

//然后你再执行下面的这个异步代码 那这个时候肯定是啥都不会执行了 因为你的状态已经被改变了,此时你已经 this.resfns.push(resfn)了这个函数,但是他压根就不会执行了

setTimeout(() => {

 promise.then(res => { 


   console.log("res3:", res) 


 }, err => { 


   console.log("err3:", err) 


 }) 

}, 1000)

执行结果:

res1: 111

res2: 111

由此可见 第三次的then 并没有执行

解决思路:

如果在then调用的时候, 状态已经确定下来 那么就应该直接执行then 里面回调即可

上代码:

class myPromise {
    constructor(executor) {
        this.status = PROMISE_STATUS_PENDING
        this.value = undefined
        this.error = undefined

        this.resfns = [] //1.多次调用then 时用数组 保存
        this.errfns = []

        const resolve = ((value) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                queueMicrotask(() => {
                    this.status = PROMISE_STATUS_FULFILLED
                    this.value = value
                    this.resfns.forEach(fn => {
                   
                        fn(this.value)
                    })
                })
            }
        })

        const reject = ((error) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                queueMicrotask(() => {
                    this.status = PROMISE_STATUS_REJECTED
                    this.error = error
                    this.errfns.forEach(fn => {
                        fn(this.error)
                    })
                })
            }
        })

        executor(resolve, reject)
    }

    then(resfn, errfn) {
        // 1.如果在then调用的时候, 状态已经确定下来,那么就可以直接调用
        if (this.status === PROMISE_STATUS_FULFILLED && resfn) {
            resfn(this.value)
        }
        if (this.status === PROMISE_STATUS_REJECTED && errfn) {
            errfn(this.error)
        }

        //2. 相反 对与已经确定的状态 就不需要在push 执行了
        if (this.status === PROMISE_STATUS_PENDING) {
            this.resfns.push(resfn)
            this.errfns.push(errfn)
        }

    }
}

执行:

const p1 = new myPromise((resolve, reject) => {

resolve(111)

// reject(333333)

})

p1.then(res => {

console.log("res1:", res)

}, err => {

console.log("err1:", err)

})

// 调用then方法多次调用

p1.then(res => {

console.log("res2:", res)

}, err => {

console.log("err2:", err)

})

// 在确定Promise状态之后, 再次调用then

setTimeout(() => {

p1.then(res => {

    console.log("res3:", res)

}, err => {

    console.log("err3:", err)

})

}, 1000)

结果:

res1: 111

res2: 111

res3: 111

三:then方法优化:

对于我们之前 的then方法多次调用是通过实例化出来的类 调用的

例如:

p1.then(res => {

console.log("res1:", res)

}, err => {

console.log("err1:", err)

})

p1.then(res => {

console.log("res2:", res)

}, err => {

console.log("err2:", err)

})

这样子调用以我们目前的代码是可以实现的,但是如果是 一直链式调用下去呢?废话少说上代码:

const p1 = new myPromise((resolve, reject) => {

resolve(111)

reject(333333)

})

p1.then(res => {

console.log("第一次的成功回调:", res)

return "第二次的成功回调"

}, err => {

console.log("err1:", err)

return "第二次的失败回调"

}).then(res => {

console.log("res2:", res)

}, err => {

console.log("err2:", err)

})

现在我们代码肯定是走不通的(可以拿去自己的环境测试下)接下来继续优化我们的代码:

优化之前首先要分析一波: (已官方的promise链式调用为示例来解释)

  1. 执行resolve

  2. 调用then 方法,then方法返回的值 实则是 new myPromse((resolve,reject) => resolve("第二次的成功回调")),将返回值 用resolve()方法再传递下去

所以在打印完 "第一次的成功回调:",111 的时候 再次打印 "res2:",第二次的成功回调,

  1. 那么什么时候才会打印 "err2:"第二次的失败回调 呢?只需要在第一次调用then 的时候 抛出异常 就会执行err2 了

例如:

p1.then(res => {

console.log("第一次的成功回调:", res)

throw new Error("err message")

}, err => {

console.log("err1:", err)

return "第二次的失败回调"

}).then(res => {

console.log("res2:", res)

}, err => {

console.log("err2:", err)

})

执行结果:

"第一次的成功回调:", 111

"err2: Error: err message

2.如果是执行reject ,在reject的第二个回调中

err => {

console.log("err1:", err)

return "第二次的失败回调"

}

return 了值那么接下来是 会执行 console.log("res2:", res) 还是 console.log("err2:", err) 呢?

例如:

const p1 = new myPromise((resolve, reject) => {

reject(333333)

})

p1.then(res => {

console.log("第一次的成功回调:", res)

return "第二次的成功回调"

}, err => {

console.log("err1:", err)

return "第二次的失败回调"

}).then(res => {

console.log("res2:", res)

}, err => {

console.log("err2:", err)

})

这里我先告诉你 其实会执行 console.log("res2:", res) ,如果想执行 console.log("err2:", err) 锁在的代码块,需要将 return "第二次的失败回调" 改为抛出异常 才会执行

栗子:

p1.then(res => {

console.log("第一次的成功回调:", res)

return "第二次的成功回调"

}, err => {

console.log("err1:", err)

throw new Error("err message")

}).then(res => {

console.log("res2:", res)

}, err => {

console.log("err2:", err)

})

逻辑已经理清楚 接下来用代码来表达:(可以复制下面这坨代码,然后用上面说的案例来自己测试 就比较清楚了)

class myPromise {
    constructor(executor) {
        this.status = PROMISE_STATUS_PENDING
        this.value = undefined
        this.error = undefined
        this.resfns = []
        this.errfns = []
        const resolve = ((value) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                queueMicrotask(() => {
                    if (this.status !== PROMISE_STATUS_PENDING) return //避免 调用resolve 后又调用 reject 多次执行
                    this.status = PROMISE_STATUS_FULFILLED
                    this.value = value
                    this.resfns.forEach(fn => {
                        fn(this.value)
                    })
                })
            }
        })

        const reject = ((error) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                queueMicrotask(() => {
                    if (this.status !== PROMISE_STATUS_PENDING) return
                    this.status = PROMISE_STATUS_REJECTED
                    this.error = error
                    this.errfns.forEach(fn => {
                        fn(this.error)
                    })
                })
            }
        })
        executor(resolve, reject)
    }

    then(resfn, errfn) {
        return new myPromise((resolve, reject) => { //1. 直接new 一个mypromise 作为then 方法的返回值 既可实现 .then.then.thne.then.....等等链式调用
            if (this.status === PROMISE_STATUS_FULFILLED && resfn) {
                try { //2. 异常处理:若成功则继续执行then链式调用 的第一个回调,失败则执行then 的第二个回调
                    const value = resfn(this.value)
                    resolve(value)
                } catch (err) {
                    reject(err)
                }
            }

            if (this.status === PROMISE_STATUS_REJECTED && errfn) {
                try {
                    const value = errfn(this.error)
                    resolve(value)
                } catch (err) {
                    reject(err)
                }
            }

            if (this.status === PROMISE_STATUS_PENDING) {
                this.resfns.push(() => { //push 回调函数
                    try {
                        const value = resfn(this.value)
                        resolve(value)
                    } catch (err) {
                        reject(err)
                    }
                })
                this.errfns.push(() => {
                    try {
                        const value = errfn(this.error)
                        resolve(value)
                    } catch (err) {
                        reject(err)
                    }
                })
            }
        })
    }
}

//然后测试:
const p1 = new myPromise((resolve, reject) => {
    // resolve(111)
    reject(333333)
})

p1.then(res => {
    console.log("res1:", res)
    return "第二次的成功回调"
}, err => {
    console.log("err1:", err)
     throw new Error("err message")
   // return "第二次的失败回调"
}).then(res => {
    console.log("res2:", res)
}, err => {
    console.log("err2:", err)
})
结果: 


err1: 333333 


err2: Error: err message 


OK 完美解决  

四:Promise-catch方法设计

 以上面的代码 我们已经实现了一个基本的promise 了 


 此时设置catch 方法  也就非常的简单了 


 上代码: 
class myPromise {
    constructor(executor) {
        this.status = PROMISE_STATUS_PENDING
        this.value = undefined
        this.error = undefined
        this.resfns = []
        this.errfns = []

        const resolve = ((value) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                queueMicrotask(() => {
                    if (this.status !== PROMISE_STATUS_PENDING) return //避免 调用resolve 后又调用 reject 多次执行
                    this.status = PROMISE_STATUS_FULFILLED
                    this.value = value
                    this.resfns.forEach(fn => {
                        fn(this.value)
                    })
                })
            }
        })

        const reject = ((error) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                queueMicrotask(() => {
                    if (this.status !== PROMISE_STATUS_PENDING) return
                    this.status = PROMISE_STATUS_REJECTED
                    this.error = error
                    this.errfns.forEach(fn => {
                        fn(this.error)
                    })
                })
            }
        })
        executor(resolve, reject)
    }

    then(resfn, errfn) {
        // 1.难点:利用抛错让下一个promise的catch帮忙处理  防止catch方法让链式调用断层
        const defaultOnRejected = err => {
            throw err
        }
        errfn = errfn || defaultOnRejected

        return new myPromise((resolve, reject) => {

            if (this.status === PROMISE_STATUS_FULFILLED && resfn) {
                try { //2. 异常处理:若成功则继续执行then链式调用 的第一个回调,失败则执行then 的第二个回调
                    const value = resfn(this.value)
                    resolve(value)
                } catch (err) {
                    reject(err)
                }
            }
            if (this.status === PROMISE_STATUS_REJECTED && errfn) {
                try {
                    const value = errfn(this.error)
                    resolve(value)
                } catch (err) {
                    reject(err)
                }
            }

            if (this.status === PROMISE_STATUS_PENDING) {
                if (resfn) {
                    this.resfns.push(() => { //push 回调函数
                        try {
                            const value = resfn(this.value)
                            resolve(value)
                        } catch (err) {
                            reject(err)     //tips:****利用抛出的错误 使得下一个promise的catch帮忙处理
                        }
                    })
                }
                if (errfn) {
                    this.errfns.push(() => {
                        try {
                            const value = errfn(this.error)
                            resolve(value)
                        } catch (err) {
                            reject(err)
                        }
                    })
                }
            }
        })
    }
    catch (errfn) { //2.catch 方法
        return this.then(undefined, errfn)
    }
}

//接着测试代码:
const p1 = new myPromise((resolve, reject) => {
    reject(333333)
})

p1.then(res => {
    console.log("res:", res)
}).catch(err => {
    console.log("err:", err)
})

//结果:
err: 333333
  这里涉及到一个难点就是  利用第一次的 p1.then的回调只传了一个参数的时候,给另外一个参数 赋值默认的函数  


  且这个函数必须抛出异常才能让 标记的tips那块代码 的catch 捕获到,然后  reject(err) 才能使用.catch 


  当然如果你想这么调用 


  p1.catch(err => { 


      console.log("err:", err) 


  }).then(res => { 


      console.log("res:", res) 


  }) 


  也只需要再赋值默认函数  让  try里面代码块   resolve(value) 接收 


  只需要在第一行注释里面 再加上这句代码即可 


  const defaultOnFulfilled = value => { 


              return value 


          } 


  resfn = resfn || defaultOnFulfilled 

五:Promise-finally方法设计

  //finally 函数无论成功或者失败 都会调用的函数  并且是在最后调用 

//直接在类上面加个finally 方法即可 利用 queueMicrotask 最快处理微任务的特性 使finally(fn) 传递过来的函数fn 最后执行

    finally(fn) { 


          setTimeout(() => { 


              fn() 


          }, 0) 


      } 


  最后附上全部代码: 
const PROMISE_STATUS_PENDING = 'pending' //定义三种状态常量
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'

class myPromise {
    constructor(executor) {
        this.status = PROMISE_STATUS_PENDING
        this.value = undefined
        this.error = undefined
        this.resfns = []
        this.errfns = []

        const resolve = ((value) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                queueMicrotask(() => {
                    if (this.status !== PROMISE_STATUS_PENDING) return //避免 调用resolve 后又调用 reject 多次执行
                    this.status = PROMISE_STATUS_FULFILLED
                    this.value = value
                    this.resfns.forEach(fn => {
                        fn(this.value)
                    })
                })
            }
        })
        const reject = ((error) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                queueMicrotask(() => {
                    if (this.status !== PROMISE_STATUS_PENDING) return
                    this.status = PROMISE_STATUS_REJECTED
                    this.error = error
                    this.errfns.forEach(fn => {
                        fn(this.error)
                    })
                })
            }
        })
        executor(resolve, reject)
    }

    then(resfn, errfn) {
        // 1.利用抛错让下一个promise的catch帮忙处理  防止catch方法让链式调用断层
        const defaultOnRejected = err => {
            throw err
        }
        errfn = errfn || defaultOnRejected

        const defaultOnFulfilled = value => {
            return value
        }
        resfn = resfn || defaultOnFulfilled

        return new myPromise((resolve, reject) => { //1. 直接new 一个mypromise 作为then 方法的返回值 既可实现 .then.then.thne.then.....等等链式调用

            if (this.status === PROMISE_STATUS_FULFILLED && resfn) {
                try { //2. 异常处理:若成功则继续执行then链式调用 的第一个回调,失败则执行then 的第二个回调
                    const value = resfn(this.value)
                    resolve(value)
                } catch (err) {
                    reject(err)
                }
            }
            if (this.status === PROMISE_STATUS_REJECTED && errfn) {
                try {
                    const value = errfn(this.error)
                    resolve(value)
                } catch (err) {
                    reject(err)
                }
            }
            if (this.status === PROMISE_STATUS_PENDING) {
                if (resfn) {
                    this.resfns.push(() => { //push 回调函数
                        try {
                            const value = resfn(this.value)
                            resolve(value)
                        } catch (err) {
                            reject(err) //tips:****利用抛出的错误 使得下一个promise的catch帮忙处理
                        }
                    })
                }
                if (errfn) {
                    this.errfns.push(() => {
                        try {
                            const value = errfn(this.error)
                            resolve(value)
                        } catch (err) {
                            reject(err)
                        }
                    })
                }
            }
        })
    }

    catch (errfn) { //2.catch 方法
        return this.then(undefined, errfn)
    }
   
    finally(fn) {
        setTimeout(() => {
            fn()
        }, 0)
    }
}

剩下的都是些类方法 比较简单 这里就先不讲了~~拜拜~

标签: javascript 前端 es6

本文转载自: https://blog.csdn.net/qq_48554377/article/details/126992966
版权归原作者 星星周z 所有, 如有侵权,请联系我们删除。

“手写Promise的基本实现 (超详细)”的评论:

还没有评论