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

目录

一:首先分析官方的promise

二:手写Promise-then方法设计

三:then方法优化:

四:Promise-catch方法设计

 五:Promise-finally方法设计


//本文带大家实现一个基本的 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 myPromise((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:",第二次的成功回调,

3. 那么什么时候才会打印 "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)
    }
}

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

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值